@flyos/design-system 3.3.0 → 3.4.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.
- package/fesm2022/flyos-design-system.mjs +757 -300
- package/fesm2022/flyos-design-system.mjs.map +1 -1
- package/package.json +1 -1
- package/scss/_fly-theme.scss +5 -0
- package/scss/_nova-tokens.scss +22 -0
- package/scss/_nova-type.scss +51 -12
- package/scss/_sf-arabic-font.scss +32 -0
- package/scss/_tokens.scss +56 -20
- package/types/flyos-design-system.d.ts +239 -61
- package/types/flyos-design-system.d.ts.map +1 -1
|
@@ -47,7 +47,7 @@ import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
|
|
47
47
|
// tools/publish-library.ps1 at bump time, and asserted by the spec beside this file.
|
|
48
48
|
// Used only for the diagnostic message; the duplicate-instance detection itself is
|
|
49
49
|
// version-agnostic, so a stale literal misnames a fork rather than hiding one.
|
|
50
|
-
const FLY_DS_VERSION = '3.
|
|
50
|
+
const FLY_DS_VERSION = '3.4.0';
|
|
51
51
|
const FLY_DS_REGISTRY_KEY = '__FLY_DS_INSTANCES__';
|
|
52
52
|
/**
|
|
53
53
|
* Records this design-system instance on the shared `scope` and returns the
|
|
@@ -1680,6 +1680,9 @@ const DS_BASELINE_LOCALES = {
|
|
|
1680
1680
|
'ui.nav.selectModule': 'Select a module',
|
|
1681
1681
|
'ui.nav.sections': 'Sections',
|
|
1682
1682
|
'ui.nav.switchModule': 'Switch module',
|
|
1683
|
+
'ui.pager.next': 'Next page',
|
|
1684
|
+
'ui.pager.page': 'Page {{page}}',
|
|
1685
|
+
'ui.pager.previous': 'Previous page',
|
|
1683
1686
|
'ui.pagination.first': 'First page',
|
|
1684
1687
|
'ui.pagination.label': 'Pagination',
|
|
1685
1688
|
'ui.pagination.last': 'Last page',
|
|
@@ -2070,6 +2073,9 @@ const DS_BASELINE_LOCALES = {
|
|
|
2070
2073
|
'ui.nav.selectModule': 'اختر وحدة',
|
|
2071
2074
|
'ui.nav.sections': 'الأقسام',
|
|
2072
2075
|
'ui.nav.switchModule': 'تبديل الوحدة',
|
|
2076
|
+
'ui.pager.next': 'الصفحة التالية',
|
|
2077
|
+
'ui.pager.page': 'الصفحة {{page}}',
|
|
2078
|
+
'ui.pager.previous': 'الصفحة السابقة',
|
|
2073
2079
|
'ui.pagination.first': 'الصفحة الأولى',
|
|
2074
2080
|
'ui.pagination.label': 'تقسيم الصفحات',
|
|
2075
2081
|
'ui.pagination.last': 'الصفحة الأخيرة',
|
|
@@ -2460,6 +2466,9 @@ const DS_BASELINE_LOCALES = {
|
|
|
2460
2466
|
'ui.nav.selectModule': 'Choisir un module',
|
|
2461
2467
|
'ui.nav.sections': 'Sections',
|
|
2462
2468
|
'ui.nav.switchModule': 'Changer de module',
|
|
2469
|
+
'ui.pager.next': 'Page suivante',
|
|
2470
|
+
'ui.pager.page': 'Page {{page}}',
|
|
2471
|
+
'ui.pager.previous': 'Page précédente',
|
|
2463
2472
|
'ui.pagination.first': 'Première page',
|
|
2464
2473
|
'ui.pagination.label': 'Pagination',
|
|
2465
2474
|
'ui.pagination.last': 'Dernière page',
|
|
@@ -2849,6 +2858,9 @@ const DS_BASELINE_LOCALES = {
|
|
|
2849
2858
|
'ui.nav.selectModule': 'ماڈیول منتخب کریں',
|
|
2850
2859
|
'ui.nav.sections': 'حصے',
|
|
2851
2860
|
'ui.nav.switchModule': 'ماڈیول تبدیل کریں',
|
|
2861
|
+
'ui.pager.next': 'اگلا صفحہ',
|
|
2862
|
+
'ui.pager.page': 'صفحہ {{page}}',
|
|
2863
|
+
'ui.pager.previous': 'پچھلا صفحہ',
|
|
2852
2864
|
'ui.pagination.first': 'پہلا صفحہ',
|
|
2853
2865
|
'ui.pagination.label': 'صفحہ بندی',
|
|
2854
2866
|
'ui.pagination.last': 'آخری صفحہ',
|
|
@@ -4008,12 +4020,12 @@ function connectRemoteLaunch(opts) {
|
|
|
4008
4020
|
const FLY_REMOTE_CONTEXT_STORE_KEY = '__flyRemoteContext__';
|
|
4009
4021
|
const FLY_REMOTE_CONTEXT_EVENT = 'fly:remote-context';
|
|
4010
4022
|
/** The single cross-bundle store, lazily created on `globalThis`. */
|
|
4011
|
-
function store$
|
|
4023
|
+
function store$2() {
|
|
4012
4024
|
const g = globalThis;
|
|
4013
4025
|
return (g[FLY_REMOTE_CONTEXT_STORE_KEY] ??= {});
|
|
4014
4026
|
}
|
|
4015
4027
|
/** Notify every federated copy of the service that the store changed. */
|
|
4016
|
-
function emitSync$
|
|
4028
|
+
function emitSync$2() {
|
|
4017
4029
|
const g = globalThis;
|
|
4018
4030
|
try {
|
|
4019
4031
|
g.dispatchEvent?.(new Event(FLY_REMOTE_CONTEXT_EVENT));
|
|
@@ -4070,33 +4082,33 @@ function toMap(s) {
|
|
|
4070
4082
|
* dispatch is ever needed.
|
|
4071
4083
|
*/
|
|
4072
4084
|
class FlyRemoteContextService {
|
|
4073
|
-
_byApp = signal(toMap(store$
|
|
4085
|
+
_byApp = signal(toMap(store$2()), ...(ngDevMode ? [{ debugName: "_byApp" }] : /* istanbul ignore next */ []));
|
|
4074
4086
|
/** All currently-published contexts, keyed by app id. Reactive. */
|
|
4075
4087
|
contexts = this._byApp.asReadonly();
|
|
4076
4088
|
constructor() {
|
|
4077
4089
|
// Keep this instance's reactive mirror current when ANOTHER federated copy of
|
|
4078
4090
|
// the service — or a remote writing the store slot directly — mutates it.
|
|
4079
4091
|
const g = globalThis;
|
|
4080
|
-
g.addEventListener?.(FLY_REMOTE_CONTEXT_EVENT, () => this._byApp.set(toMap(store$
|
|
4092
|
+
g.addEventListener?.(FLY_REMOTE_CONTEXT_EVENT, () => this._byApp.set(toMap(store$2())));
|
|
4081
4093
|
}
|
|
4082
4094
|
/** Publish (or replace) the active route context for an app. */
|
|
4083
4095
|
publish(ctx) {
|
|
4084
|
-
store$
|
|
4085
|
-
this._byApp.set(toMap(store$
|
|
4086
|
-
emitSync$
|
|
4096
|
+
store$2()[ctx.appId] = ctx;
|
|
4097
|
+
this._byApp.set(toMap(store$2()));
|
|
4098
|
+
emitSync$2();
|
|
4087
4099
|
}
|
|
4088
4100
|
/** Drop an app's context (remote unmounted / window closed). No-op if absent. */
|
|
4089
4101
|
clear(appId) {
|
|
4090
|
-
const s = store$
|
|
4102
|
+
const s = store$2();
|
|
4091
4103
|
if (!(appId in s))
|
|
4092
4104
|
return;
|
|
4093
4105
|
delete s[appId];
|
|
4094
4106
|
this._byApp.set(toMap(s));
|
|
4095
|
-
emitSync$
|
|
4107
|
+
emitSync$2();
|
|
4096
4108
|
}
|
|
4097
4109
|
/** Current context for an app, or `null` when nothing is published. */
|
|
4098
4110
|
context(appId) {
|
|
4099
|
-
return store$
|
|
4111
|
+
return store$2()[appId] ?? null;
|
|
4100
4112
|
}
|
|
4101
4113
|
/**
|
|
4102
4114
|
* Resolve a single route param for an app, e.g. `param('circles', 'id')`.
|
|
@@ -4104,7 +4116,7 @@ class FlyRemoteContextService {
|
|
|
4104
4116
|
* the caller then degrades (the skill asks for the missing id).
|
|
4105
4117
|
*/
|
|
4106
4118
|
param(appId, path) {
|
|
4107
|
-
const value = store$
|
|
4119
|
+
const value = store$2()[appId]?.params[path];
|
|
4108
4120
|
return value != null && value !== '' ? value : null;
|
|
4109
4121
|
}
|
|
4110
4122
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlyRemoteContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -9971,15 +9983,144 @@ class FlyBreadcrumbComponent {
|
|
|
9971
9983
|
this.navigate.emit(item);
|
|
9972
9984
|
}
|
|
9973
9985
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlyBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9974
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlyBreadcrumbComponent, isStandalone: true, selector: "fly-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, maxVisible: { classPropertyName: "maxVisible", publicName: "maxVisible", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hasCurrentPage: { classPropertyName: "hasCurrentPage", publicName: "hasCurrentPage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navigate: "navigate" }, host: { properties: { "attr.dir": "i18n.direction()" } }, ngImport: i0, template: "<!--\r\n The CURRENT-PAGE crumb is ALWAYS plain text with aria-current=\"page\" \u2014 never an <a>\r\n or <button>, even when it carries an href. With [hasCurrentPage]=\"false\" the trail\r\n lists ancestors only, so no crumb is the current page and every one stays a link.\r\n The separator is decorative (aria-hidden) and drawn as a CSS mask so it mirrors\r\n under RTL with no directional glyph baked in.\r\n-->\r\n<nav class=\"fly-breadcrumb\" [attr.aria-label]=\"resolvedAriaLabel()\">\r\n
|
|
9986
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlyBreadcrumbComponent, isStandalone: true, selector: "fly-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, maxVisible: { classPropertyName: "maxVisible", publicName: "maxVisible", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hasCurrentPage: { classPropertyName: "hasCurrentPage", publicName: "hasCurrentPage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navigate: "navigate" }, host: { properties: { "attr.dir": "i18n.direction()" } }, ngImport: i0, template: "<!--\r\n The CURRENT-PAGE crumb is ALWAYS plain text with aria-current=\"page\" \u2014 never an <a>\r\n or <button>, even when it carries an href. With [hasCurrentPage]=\"false\" the trail\r\n lists ancestors only, so no crumb is the current page and every one stays a link.\r\n The separator is decorative (aria-hidden) and drawn as a CSS mask so it mirrors\r\n under RTL with no directional glyph baked in.\r\n-->\r\n<nav class=\"fly-breadcrumb\" [attr.aria-label]=\"resolvedAriaLabel()\">\r\n @for (entry of renderItems(); track entry.kind === 'item' ? entry.item.id : 'overflow'; let i = $index) {\r\n @if (i > 0) {\r\n <svg class=\"fly-breadcrumb__separator\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\">\r\n <path d=\"M9 5l7 7-7 7\" />\r\n </svg>\r\n }\r\n @if (entry.kind === 'overflow') {\r\n <span class=\"fly-breadcrumb__crumb fly-breadcrumb__crumb--overflow\">\u2026</span>\r\n } @else if (entry.isCurrent) {\r\n <span class=\"fly-breadcrumb__crumb fly-breadcrumb__crumb--current\" aria-current=\"page\">\r\n @if (entry.item.iconPath; as d) {\r\n <svg class=\"fly-breadcrumb__icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\"><path [attr.d]=\"d\" /></svg>\r\n }\r\n {{ entry.item.label }}\r\n </span>\r\n } @else {\r\n <span class=\"fly-breadcrumb__crumb\">\r\n @if (entry.item.href) {\r\n <a class=\"fly-breadcrumb__link\" [attr.href]=\"entry.item.href\"\r\n (click)=\"onActivate(entry.item)\">\r\n @if (entry.item.iconPath; as d) {\r\n <svg class=\"fly-breadcrumb__icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\"><path [attr.d]=\"d\" /></svg>\r\n }\r\n {{ entry.item.label }}\r\n </a>\r\n } @else {\r\n <button type=\"button\" class=\"fly-breadcrumb__link fly-breadcrumb__link--button\"\r\n (click)=\"onActivate(entry.item)\">\r\n @if (entry.item.iconPath; as d) {\r\n <svg class=\"fly-breadcrumb__icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\"><path [attr.d]=\"d\" /></svg>\r\n }\r\n {{ entry.item.label }}\r\n </button>\r\n }\r\n </span>\r\n }\r\n }\r\n</nav>\r\n", styles: [":host{display:block;min-inline-size:0}.fly-breadcrumb{display:flex;align-items:center;gap:2px;min-inline-size:0}.fly-breadcrumb__crumb{display:flex;align-items:center;flex:none;min-inline-size:0;font-size:12.5px;line-height:1.4}.fly-breadcrumb__crumb--current{flex:1 1 auto;min-inline-size:0;gap:6px;block-size:26px;padding-inline:4px;font-weight:600;letter-spacing:-.006em;color:var(--text-color);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host([dir=rtl]) .fly-breadcrumb__crumb--current{letter-spacing:normal}.fly-breadcrumb__crumb--overflow{color:var(--text-color-secondary);-webkit-user-select:none;user-select:none}.fly-breadcrumb__link{display:inline-flex;align-items:center;gap:6px;block-size:26px;padding-inline:8px;margin:0;border:0;border-radius:8px;background:transparent;color:var(--text-color-secondary);font-family:inherit;font-size:12.5px;font-weight:500;line-height:1;text-decoration:none;cursor:pointer;max-inline-size:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:background var(--nova-duration-hover) var(--nova-ease-micro),color var(--nova-duration-hover) var(--nova-ease-micro)}.fly-breadcrumb__link:hover{background:var(--w08);color:var(--text-color)}.fly-breadcrumb__link:focus-visible{outline:2px solid var(--primary-color);outline-offset:1px}.fly-breadcrumb__link--button{display:inline-flex}.fly-breadcrumb__icon{flex:none}.fly-breadcrumb__separator{flex:none;color:var(--w28)}:host([dir=rtl]) .fly-breadcrumb__separator{transform:scaleX(-1)}@media(forced-colors:active){.fly-breadcrumb__separator{color:CanvasText}.fly-breadcrumb__link:focus-visible{outline-color:Highlight}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9975
9987
|
}
|
|
9976
9988
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlyBreadcrumbComponent, decorators: [{
|
|
9977
9989
|
type: Component,
|
|
9978
9990
|
args: [{ selector: 'fly-breadcrumb', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
9979
9991
|
'[attr.dir]': 'i18n.direction()',
|
|
9980
|
-
}, template: "<!--\r\n The CURRENT-PAGE crumb is ALWAYS plain text with aria-current=\"page\" \u2014 never an <a>\r\n or <button>, even when it carries an href. With [hasCurrentPage]=\"false\" the trail\r\n lists ancestors only, so no crumb is the current page and every one stays a link.\r\n The separator is decorative (aria-hidden) and drawn as a CSS mask so it mirrors\r\n under RTL with no directional glyph baked in.\r\n-->\r\n<nav class=\"fly-breadcrumb\" [attr.aria-label]=\"resolvedAriaLabel()\">\r\n
|
|
9992
|
+
}, template: "<!--\r\n The CURRENT-PAGE crumb is ALWAYS plain text with aria-current=\"page\" \u2014 never an <a>\r\n or <button>, even when it carries an href. With [hasCurrentPage]=\"false\" the trail\r\n lists ancestors only, so no crumb is the current page and every one stays a link.\r\n The separator is decorative (aria-hidden) and drawn as a CSS mask so it mirrors\r\n under RTL with no directional glyph baked in.\r\n-->\r\n<nav class=\"fly-breadcrumb\" [attr.aria-label]=\"resolvedAriaLabel()\">\r\n @for (entry of renderItems(); track entry.kind === 'item' ? entry.item.id : 'overflow'; let i = $index) {\r\n @if (i > 0) {\r\n <svg class=\"fly-breadcrumb__separator\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\">\r\n <path d=\"M9 5l7 7-7 7\" />\r\n </svg>\r\n }\r\n @if (entry.kind === 'overflow') {\r\n <span class=\"fly-breadcrumb__crumb fly-breadcrumb__crumb--overflow\">\u2026</span>\r\n } @else if (entry.isCurrent) {\r\n <span class=\"fly-breadcrumb__crumb fly-breadcrumb__crumb--current\" aria-current=\"page\">\r\n @if (entry.item.iconPath; as d) {\r\n <svg class=\"fly-breadcrumb__icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\"><path [attr.d]=\"d\" /></svg>\r\n }\r\n {{ entry.item.label }}\r\n </span>\r\n } @else {\r\n <span class=\"fly-breadcrumb__crumb\">\r\n @if (entry.item.href) {\r\n <a class=\"fly-breadcrumb__link\" [attr.href]=\"entry.item.href\"\r\n (click)=\"onActivate(entry.item)\">\r\n @if (entry.item.iconPath; as d) {\r\n <svg class=\"fly-breadcrumb__icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\"><path [attr.d]=\"d\" /></svg>\r\n }\r\n {{ entry.item.label }}\r\n </a>\r\n } @else {\r\n <button type=\"button\" class=\"fly-breadcrumb__link fly-breadcrumb__link--button\"\r\n (click)=\"onActivate(entry.item)\">\r\n @if (entry.item.iconPath; as d) {\r\n <svg class=\"fly-breadcrumb__icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\"><path [attr.d]=\"d\" /></svg>\r\n }\r\n {{ entry.item.label }}\r\n </button>\r\n }\r\n </span>\r\n }\r\n }\r\n</nav>\r\n", styles: [":host{display:block;min-inline-size:0}.fly-breadcrumb{display:flex;align-items:center;gap:2px;min-inline-size:0}.fly-breadcrumb__crumb{display:flex;align-items:center;flex:none;min-inline-size:0;font-size:12.5px;line-height:1.4}.fly-breadcrumb__crumb--current{flex:1 1 auto;min-inline-size:0;gap:6px;block-size:26px;padding-inline:4px;font-weight:600;letter-spacing:-.006em;color:var(--text-color);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host([dir=rtl]) .fly-breadcrumb__crumb--current{letter-spacing:normal}.fly-breadcrumb__crumb--overflow{color:var(--text-color-secondary);-webkit-user-select:none;user-select:none}.fly-breadcrumb__link{display:inline-flex;align-items:center;gap:6px;block-size:26px;padding-inline:8px;margin:0;border:0;border-radius:8px;background:transparent;color:var(--text-color-secondary);font-family:inherit;font-size:12.5px;font-weight:500;line-height:1;text-decoration:none;cursor:pointer;max-inline-size:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:background var(--nova-duration-hover) var(--nova-ease-micro),color var(--nova-duration-hover) var(--nova-ease-micro)}.fly-breadcrumb__link:hover{background:var(--w08);color:var(--text-color)}.fly-breadcrumb__link:focus-visible{outline:2px solid var(--primary-color);outline-offset:1px}.fly-breadcrumb__link--button{display:inline-flex}.fly-breadcrumb__icon{flex:none}.fly-breadcrumb__separator{flex:none;color:var(--w28)}:host([dir=rtl]) .fly-breadcrumb__separator{transform:scaleX(-1)}@media(forced-colors:active){.fly-breadcrumb__separator{color:CanvasText}.fly-breadcrumb__link:focus-visible{outline-color:Highlight}}\n"] }]
|
|
9981
9993
|
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], maxVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxVisible", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hasCurrentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasCurrentPage", required: false }] }], navigate: [{ type: i0.Output, args: ["navigate"] }] } });
|
|
9982
9994
|
|
|
9995
|
+
/** Cross-bundle store key + sync event — same shape as `FLY_MAGIC_BAR_STORE_KEY`. */
|
|
9996
|
+
const FLY_WINDOW_BREADCRUMBS_STORE_KEY = '__flyWindowBreadcrumbs__';
|
|
9997
|
+
const FLY_WINDOW_BREADCRUMBS_EVENT = 'fly:window-breadcrumbs';
|
|
9998
|
+
function store$1() {
|
|
9999
|
+
const g = globalThis;
|
|
10000
|
+
return (g[FLY_WINDOW_BREADCRUMBS_STORE_KEY] ??= { trails: {}, revision: 0 });
|
|
10001
|
+
}
|
|
10002
|
+
function emitSync$1() {
|
|
10003
|
+
const g = globalThis;
|
|
10004
|
+
try {
|
|
10005
|
+
g.dispatchEvent?.(new Event(FLY_WINDOW_BREADCRUMBS_EVENT));
|
|
10006
|
+
}
|
|
10007
|
+
catch {
|
|
10008
|
+
// Non-DOM realm (SSR / a unit env without `Event`) — see the identical note on
|
|
10009
|
+
// `MagicBarRegistry.emitSync`. The instance that mutated the store is
|
|
10010
|
+
// unaffected; any OTHER instance in such a realm stays stale until its own
|
|
10011
|
+
// next mutation calls `sync()` directly.
|
|
10012
|
+
}
|
|
10013
|
+
}
|
|
10014
|
+
/**
|
|
10015
|
+
* Where an app publishes the breadcrumb trail its window's header strip renders.
|
|
10016
|
+
*
|
|
10017
|
+
* The design puts a trail inside the window
|
|
10018
|
+
* (`UX/FlyOS Desktop-app/MainContent.dc.html`, `nav[aria-label="Breadcrumb"]`), and
|
|
10019
|
+
* the window chrome (`WindowComponent`) renders whatever this registry holds for
|
|
10020
|
+
* its window id, falling back to the plain window title when nothing has published.
|
|
10021
|
+
*
|
|
10022
|
+
* ## Why the state lives on `globalThis`
|
|
10023
|
+
*
|
|
10024
|
+
* Same reasoning as {@link MagicBarRegistry} (`services/magic-bar/`), and the same
|
|
10025
|
+
* direction of travel (remote → shell chrome). This service is `providedIn: 'root'`,
|
|
10026
|
+
* but a root instance is not *reliably* shared across the federation boundary: the
|
|
10027
|
+
* shell builds the design system from workspace source while remotes consume the
|
|
10028
|
+
* published package, and Native Federation collapses those into one runtime
|
|
10029
|
+
* instance only when version negotiation succeeds — a split there is invisible at
|
|
10030
|
+
* build time and would otherwise present as a window whose breadcrumb silently
|
|
10031
|
+
* never appears, for federated apps only. Keeping the state on the one substrate
|
|
10032
|
+
* every bundle shares makes this store fork-proof: whichever copy of this service
|
|
10033
|
+
* publishes, every copy's `trail()` agrees. Each instance mirrors the store into a
|
|
10034
|
+
* local signal and re-reads on the sync event, so `WindowComponent`'s template —
|
|
10035
|
+
* which reads `trail()` inside its own reactive context — repaints whichever
|
|
10036
|
+
* bundle actually published.
|
|
10037
|
+
*
|
|
10038
|
+
* Simpler than `MagicBarRegistry` on purpose: a breadcrumb trail is addressed
|
|
10039
|
+
* directly by the window id its publisher already has (via `WINDOW_DATA`), so
|
|
10040
|
+
* there is no owner-vs-active-window arbitration, no per-action indexing and no
|
|
10041
|
+
* publish-fingerprint dedup to carry — `publish()` just replaces the entry.
|
|
10042
|
+
*
|
|
10043
|
+
* ```ts
|
|
10044
|
+
* const win = inject(WINDOW_DATA);
|
|
10045
|
+
* const crumbs = inject(WindowBreadcrumbsRegistry);
|
|
10046
|
+
* crumbs.publish(win.id, [
|
|
10047
|
+
* { id: 'root', label: circleName, navigate: () => this.showList() },
|
|
10048
|
+
* { id: 'trend', label: trend.title },
|
|
10049
|
+
* ]);
|
|
10050
|
+
* ```
|
|
10051
|
+
*/
|
|
10052
|
+
class WindowBreadcrumbsRegistry {
|
|
10053
|
+
_trails = signal(new Map(), ...(ngDevMode ? [{ debugName: "_trails" }] : /* istanbul ignore next */ []));
|
|
10054
|
+
_syncedStore = null;
|
|
10055
|
+
_syncedRevision = -1;
|
|
10056
|
+
/** Every published trail, keyed by window id. Reactive — read it inside a template/computed/effect. */
|
|
10057
|
+
all = this._trails.asReadonly();
|
|
10058
|
+
constructor() {
|
|
10059
|
+
const g = globalThis;
|
|
10060
|
+
g.addEventListener?.(FLY_WINDOW_BREADCRUMBS_EVENT, () => this.sync());
|
|
10061
|
+
this.sync();
|
|
10062
|
+
}
|
|
10063
|
+
/**
|
|
10064
|
+
* This window's published trail, or an empty array.
|
|
10065
|
+
*
|
|
10066
|
+
* A plain method over a signal read, not a per-id `computed`: it is called from
|
|
10067
|
+
* the window template, where the signal read is tracked whatever the call depth,
|
|
10068
|
+
* and a `computed` cache keyed by a mutable window id would outlive its window.
|
|
10069
|
+
*/
|
|
10070
|
+
trail(windowId) {
|
|
10071
|
+
return this._trails().get(windowId) ?? EMPTY_TRAIL;
|
|
10072
|
+
}
|
|
10073
|
+
/**
|
|
10074
|
+
* Replace a window's trail. Publishing an empty array is how an app goes back to
|
|
10075
|
+
* showing the plain window title — it is a state, not a clear.
|
|
10076
|
+
*/
|
|
10077
|
+
publish(windowId, crumbs) {
|
|
10078
|
+
const s = store$1();
|
|
10079
|
+
s.trails = { ...s.trails, [windowId]: [...crumbs] };
|
|
10080
|
+
s.revision++;
|
|
10081
|
+
this.sync();
|
|
10082
|
+
emitSync$1();
|
|
10083
|
+
}
|
|
10084
|
+
/** Drop a window's trail entirely. The window chrome calls this when it is destroyed. */
|
|
10085
|
+
clear(windowId) {
|
|
10086
|
+
const s = store$1();
|
|
10087
|
+
if (!(windowId in s.trails))
|
|
10088
|
+
return;
|
|
10089
|
+
const trails = { ...s.trails };
|
|
10090
|
+
delete trails[windowId];
|
|
10091
|
+
s.trails = trails;
|
|
10092
|
+
s.revision++;
|
|
10093
|
+
this.sync();
|
|
10094
|
+
emitSync$1();
|
|
10095
|
+
}
|
|
10096
|
+
/**
|
|
10097
|
+
* Re-read the shared store. Cheap no-op unless a real mutation landed.
|
|
10098
|
+
*
|
|
10099
|
+
* Keys on store IDENTITY as well as revision — see the identical note on
|
|
10100
|
+
* `MagicBarRegistry.sync`: a revision counter alone cannot distinguish "nothing
|
|
10101
|
+
* changed" from "the `globalThis` slot now holds a brand-new store that happens
|
|
10102
|
+
* to be at the same revision" (every spec's `beforeEach` replaces the store).
|
|
10103
|
+
*/
|
|
10104
|
+
sync() {
|
|
10105
|
+
const s = store$1();
|
|
10106
|
+
if (s === this._syncedStore && s.revision === this._syncedRevision)
|
|
10107
|
+
return;
|
|
10108
|
+
this._syncedStore = s;
|
|
10109
|
+
this._syncedRevision = s.revision;
|
|
10110
|
+
this._trails.set(new Map(Object.entries(s.trails)));
|
|
10111
|
+
}
|
|
10112
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: WindowBreadcrumbsRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
10113
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: WindowBreadcrumbsRegistry, providedIn: 'root' });
|
|
10114
|
+
}
|
|
10115
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: WindowBreadcrumbsRegistry, decorators: [{
|
|
10116
|
+
type: Injectable,
|
|
10117
|
+
args: [{ providedIn: 'root' }]
|
|
10118
|
+
}], ctorParameters: () => [] });
|
|
10119
|
+
/** Shared empty result — a fresh `[]` per read would re-render every window on every pass. */
|
|
10120
|
+
const EMPTY_TRAIL = Object.freeze([]);
|
|
10121
|
+
|
|
10122
|
+
/** Public window-breadcrumbs surface — see `window-breadcrumbs-registry.service.ts`. */
|
|
10123
|
+
|
|
9983
10124
|
/**
|
|
9984
10125
|
* Headless keyboard-navigation logic — roving focus for menus, radiogroups, and
|
|
9985
10126
|
* tablists.
|
|
@@ -12768,6 +12909,53 @@ function resolveLinks(deps, renderableIds) {
|
|
|
12768
12909
|
}
|
|
12769
12910
|
return out;
|
|
12770
12911
|
}
|
|
12912
|
+
/** Straight run (px) a dependency elbow travels out of a bar edge before it turns. */
|
|
12913
|
+
const LINK_STUB = 12;
|
|
12914
|
+
/**
|
|
12915
|
+
* Relationship kind implied by the edge a gesture left and the edge it landed on. The name is
|
|
12916
|
+
* literally `<predecessor endpoint><successor endpoint>`, so this is a lookup, not a rule.
|
|
12917
|
+
*/
|
|
12918
|
+
function linkTypeFor(from, to) {
|
|
12919
|
+
if (from === 'finish')
|
|
12920
|
+
return to === 'start' ? 'FS' : 'FF';
|
|
12921
|
+
return to === 'start' ? 'SS' : 'SF';
|
|
12922
|
+
}
|
|
12923
|
+
/**
|
|
12924
|
+
* Route a dependency elbow for any of the four relationship kinds (render space).
|
|
12925
|
+
*
|
|
12926
|
+
* The kind names the two endpoints, so it decides both the anchor x's and the direction the
|
|
12927
|
+
* arrow travels: leaving a *finish* edge moves forward in time, leaving a *start* edge moves
|
|
12928
|
+
* backward; entering a *start* edge approaches from behind, entering a *finish* edge from
|
|
12929
|
+
* ahead. `fwd` folds RTL in, so none of that branches on direction.
|
|
12930
|
+
*
|
|
12931
|
+
* One turn suffices when the entry stub lies ahead of the exit stub in the approach
|
|
12932
|
+
* direction. When it does not — overlapping or reversed bars, which SS/FF/SF hit constantly —
|
|
12933
|
+
* a straight drop would cut back through the bars, so the elbow detours via the gap between
|
|
12934
|
+
* the two rows instead.
|
|
12935
|
+
*/
|
|
12936
|
+
function routeLink(from, to, type, rtl, rowHeight) {
|
|
12937
|
+
const fwd = rtl ? -1 : 1;
|
|
12938
|
+
const fromFinish = type === 'FS' || type === 'FF';
|
|
12939
|
+
const toStart = type === 'FS' || type === 'SS';
|
|
12940
|
+
const x1 = from.shape === 'milestone' ? from.pointX : fromFinish ? from.endX : from.startX;
|
|
12941
|
+
const x2 = to.shape === 'milestone' ? to.pointX : toStart ? to.startX : to.endX;
|
|
12942
|
+
const y1 = from.midY;
|
|
12943
|
+
const y2 = to.midY;
|
|
12944
|
+
const d1 = (fromFinish ? 1 : -1) * fwd; // exit direction
|
|
12945
|
+
const d2 = (toStart ? 1 : -1) * fwd; // approach direction
|
|
12946
|
+
const exit = x1 + LINK_STUB * d1;
|
|
12947
|
+
const entry = x2 - LINK_STUB * d2;
|
|
12948
|
+
if ((entry - exit) * d2 >= 0) {
|
|
12949
|
+
return { path: `M ${x1} ${y1} H ${exit} V ${y2} H ${x2}`, badgeX: exit, badgeY: (y1 + y2) / 2 };
|
|
12950
|
+
}
|
|
12951
|
+
// Distinct rows always differ in y; the guard only keeps a degenerate input off the bar.
|
|
12952
|
+
const yMid = y1 === y2 ? y1 - rowHeight / 2 : (y1 + y2) / 2;
|
|
12953
|
+
return {
|
|
12954
|
+
path: `M ${x1} ${y1} H ${exit} V ${yMid} H ${entry} V ${y2} H ${x2}`,
|
|
12955
|
+
badgeX: entry,
|
|
12956
|
+
badgeY: yMid,
|
|
12957
|
+
};
|
|
12958
|
+
}
|
|
12771
12959
|
// ── RTL coordinate mapping ───────────────────────────────────────────────────
|
|
12772
12960
|
/** Mirror a logical x into render space. LTR is identity; RTL reflects about the canvas width. */
|
|
12773
12961
|
function mapX(logicalX, innerWidth, rtl) {
|
|
@@ -12868,6 +13056,257 @@ function weekendBands(domain, zoom, pxPerDay) {
|
|
|
12868
13056
|
return bands;
|
|
12869
13057
|
}
|
|
12870
13058
|
|
|
13059
|
+
/** Label-pane width bounds (px) the splitter clamps to. */
|
|
13060
|
+
const GANTT_LABEL_W_MIN = 140;
|
|
13061
|
+
const GANTT_LABEL_W_MAX = 720;
|
|
13062
|
+
/** Keyboard resize steps (plain arrow / with Shift). */
|
|
13063
|
+
const LABEL_STEP = 16;
|
|
13064
|
+
const LABEL_STEP_COARSE = 64;
|
|
13065
|
+
/** Clamp any candidate label-pane width into the splitter's range. */
|
|
13066
|
+
function clampGanttLabelWidth(px) {
|
|
13067
|
+
return Math.max(GANTT_LABEL_W_MIN, Math.min(GANTT_LABEL_W_MAX, Math.round(px)));
|
|
13068
|
+
}
|
|
13069
|
+
/**
|
|
13070
|
+
* The Gantt's label-pane divider: width state plus the pointer and keyboard resize gestures.
|
|
13071
|
+
*
|
|
13072
|
+
* ## Why this is not in the component
|
|
13073
|
+
* `fly-gantt` is a large component by nature — it owns a two-pane layout, a time scale, four row
|
|
13074
|
+
* shapes, drag/resize/link gestures and dependency routing. The pane divider is the one region
|
|
13075
|
+
* with *no* coupling to any of that: it needs a direction, a fallback width and somewhere to
|
|
13076
|
+
* report a committed value, and nothing else. Keeping it here means the component's remaining
|
|
13077
|
+
* bulk is all chart, and the divider's RTL rules — which are the subtle part — are stated in one
|
|
13078
|
+
* place and unit-testable without rendering an SVG.
|
|
13079
|
+
*
|
|
13080
|
+
* ## The RTL rule, once
|
|
13081
|
+
* The label pane sits on the inline-START side in both directions, so "drag outward = wider"
|
|
13082
|
+
* flips physical sign with direction, and the arrow key that widens is the one pointing AWAY
|
|
13083
|
+
* from the pane (`ArrowRight` in LTR, `ArrowLeft` in RTL). Both facts are encoded here rather
|
|
13084
|
+
* than at each call site, which is what stops one of the two gestures being mirrored and the
|
|
13085
|
+
* other not — the failure this consolidation exists to prevent.
|
|
13086
|
+
*/
|
|
13087
|
+
class GanttLabelPane {
|
|
13088
|
+
deps;
|
|
13089
|
+
/** True for the duration of a pointer drag — the host paints a resizing affordance. */
|
|
13090
|
+
resizing = signal(false, ...(ngDevMode ? [{ debugName: "resizing" }] : /* istanbul ignore next */ []));
|
|
13091
|
+
/** The user's dragged width, or `null` while the consumer's input is in charge. */
|
|
13092
|
+
_override = signal(null, ...(ngDevMode ? [{ debugName: "_override" }] : /* istanbul ignore next */ []));
|
|
13093
|
+
/** The width to render: the user's drag when present, else the clamped input. */
|
|
13094
|
+
width = computed(() => clampGanttLabelWidth(this._override() ?? this.deps.inputWidth()), ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
13095
|
+
constructor(deps) {
|
|
13096
|
+
this.deps = deps;
|
|
13097
|
+
}
|
|
13098
|
+
/**
|
|
13099
|
+
* Pointer-drag the divider. Forward-in-time is `+x` LTR and `-x` RTL, and the label pane sits
|
|
13100
|
+
* on the inline-start side in both, so the same sign flip that mirrors the timeline also
|
|
13101
|
+
* mirrors "drag outward = wider".
|
|
13102
|
+
*
|
|
13103
|
+
* Listeners go on `document`, not the divider: a fast drag outruns the 8px-wide handle, and a
|
|
13104
|
+
* pointer that leaves it would otherwise strand the gesture mid-resize.
|
|
13105
|
+
*/
|
|
13106
|
+
onPointerDown(ev) {
|
|
13107
|
+
if (!this.deps.resizable() || ev.button !== 0)
|
|
13108
|
+
return;
|
|
13109
|
+
ev.preventDefault();
|
|
13110
|
+
ev.stopPropagation();
|
|
13111
|
+
const startClientX = ev.clientX;
|
|
13112
|
+
const startWidth = this.width();
|
|
13113
|
+
const dirSign = this.deps.rtl() ? -1 : 1;
|
|
13114
|
+
this.resizing.set(true);
|
|
13115
|
+
const onMove = (e) => {
|
|
13116
|
+
this._override.set(clampGanttLabelWidth(startWidth + (e.clientX - startClientX) * dirSign));
|
|
13117
|
+
};
|
|
13118
|
+
const onUp = () => {
|
|
13119
|
+
document.removeEventListener('pointermove', onMove);
|
|
13120
|
+
document.removeEventListener('pointerup', onUp);
|
|
13121
|
+
this.resizing.set(false);
|
|
13122
|
+
const final = this.width();
|
|
13123
|
+
// Only a width that actually MOVED is committed: a plain click on the divider must not
|
|
13124
|
+
// emit, or a consumer persisting the value would write on every stray press.
|
|
13125
|
+
if (final !== startWidth)
|
|
13126
|
+
this.deps.commit(final);
|
|
13127
|
+
};
|
|
13128
|
+
document.addEventListener('pointermove', onMove);
|
|
13129
|
+
document.addEventListener('pointerup', onUp);
|
|
13130
|
+
this.deps.destroyRef.onDestroy(() => {
|
|
13131
|
+
document.removeEventListener('pointermove', onMove);
|
|
13132
|
+
document.removeEventListener('pointerup', onUp);
|
|
13133
|
+
});
|
|
13134
|
+
}
|
|
13135
|
+
/**
|
|
13136
|
+
* Keyboard resize on the focused divider. ArrowRight/Left grow/shrink by the *inline* meaning
|
|
13137
|
+
* of the key, so under RTL the arrow that points away from the pane is still the one that
|
|
13138
|
+
* widens it. `Home`/`End` jump to the bounds, Enter/Escape restore the input width.
|
|
13139
|
+
*
|
|
13140
|
+
* Typed `Event` because Angular's strict template checker types a `(keydown)` `$event` as
|
|
13141
|
+
* `Event`; the cast is done here once rather than at the binding.
|
|
13142
|
+
*/
|
|
13143
|
+
onKeydown(ev) {
|
|
13144
|
+
if (!this.deps.resizable())
|
|
13145
|
+
return;
|
|
13146
|
+
const key = ev.key;
|
|
13147
|
+
const coarse = ev.shiftKey;
|
|
13148
|
+
const step = coarse ? LABEL_STEP_COARSE : LABEL_STEP;
|
|
13149
|
+
const rtl = this.deps.rtl();
|
|
13150
|
+
const grow = rtl ? 'ArrowLeft' : 'ArrowRight';
|
|
13151
|
+
const shrink = rtl ? 'ArrowRight' : 'ArrowLeft';
|
|
13152
|
+
let next;
|
|
13153
|
+
if (key === grow)
|
|
13154
|
+
next = this.width() + step;
|
|
13155
|
+
else if (key === shrink)
|
|
13156
|
+
next = this.width() - step;
|
|
13157
|
+
else if (key === 'Home')
|
|
13158
|
+
next = GANTT_LABEL_W_MIN;
|
|
13159
|
+
else if (key === 'End')
|
|
13160
|
+
next = GANTT_LABEL_W_MAX;
|
|
13161
|
+
else if (key === 'Enter' || key === 'Escape') {
|
|
13162
|
+
// Stop the grid-level Enter handler from also activating the selected row.
|
|
13163
|
+
ev.preventDefault();
|
|
13164
|
+
ev.stopPropagation();
|
|
13165
|
+
this.reset();
|
|
13166
|
+
return;
|
|
13167
|
+
}
|
|
13168
|
+
else
|
|
13169
|
+
return;
|
|
13170
|
+
ev.preventDefault();
|
|
13171
|
+
ev.stopPropagation();
|
|
13172
|
+
const clamped = clampGanttLabelWidth(next);
|
|
13173
|
+
// Already at a bound: swallow the key (it was ours) but emit nothing.
|
|
13174
|
+
if (clamped === this.width())
|
|
13175
|
+
return;
|
|
13176
|
+
this._override.set(clamped);
|
|
13177
|
+
this.deps.commit(clamped);
|
|
13178
|
+
}
|
|
13179
|
+
/** Drop the user's dragged width and fall back to the consumer's input width. */
|
|
13180
|
+
reset() {
|
|
13181
|
+
if (this._override() === null)
|
|
13182
|
+
return;
|
|
13183
|
+
this._override.set(null);
|
|
13184
|
+
this.deps.commit(this.width());
|
|
13185
|
+
}
|
|
13186
|
+
}
|
|
13187
|
+
|
|
13188
|
+
/**
|
|
13189
|
+
* Drawing a dependency by dragging from one end of a bar to another — the gesture, the drop-target
|
|
13190
|
+
* resolution and the resulting relationship type.
|
|
13191
|
+
*
|
|
13192
|
+
* ## Why this is not in the component
|
|
13193
|
+
* `fly-gantt` already owns a two-pane layout, a time scale, four row shapes and bar move/resize.
|
|
13194
|
+
* This is a self-contained second gesture with one output, and its interesting part is a *rule*
|
|
13195
|
+
* rather than any rendering: which end you START on plus which end you DROP nearest decides which
|
|
13196
|
+
* of the four MS-Project relationships you get. Stating that rule beside the hit-testing that
|
|
13197
|
+
* feeds it — rather than in the middle of a 700-line component — is what makes it reviewable.
|
|
13198
|
+
*
|
|
13199
|
+
* ## The selection is here too, and deliberately
|
|
13200
|
+
* `selectedKey` is the link the user has clicked, which is what the delete affordance and the
|
|
13201
|
+
* keyboard's Delete both act on. It lives with the gesture because both are "the user is
|
|
13202
|
+
* manipulating dependencies", and keeping them together stops a click-selection surviving a drag
|
|
13203
|
+
* that replaced the very link it pointed at.
|
|
13204
|
+
*/
|
|
13205
|
+
class GanttLinkGestures {
|
|
13206
|
+
deps;
|
|
13207
|
+
/** The in-flight drag, or `null`. */
|
|
13208
|
+
gesture = signal(null, ...(ngDevMode ? [{ debugName: "gesture" }] : /* istanbul ignore next */ []));
|
|
13209
|
+
/** The clicked link's key, or `null`. Drives the delete affordance and the Delete key. */
|
|
13210
|
+
selectedKey = signal(null, ...(ngDevMode ? [{ debugName: "selectedKey" }] : /* istanbul ignore next */ []));
|
|
13211
|
+
/** Rubber-band path for the in-flight gesture (render space), or `null` when idle. */
|
|
13212
|
+
path = computed(() => {
|
|
13213
|
+
const g = this.gesture();
|
|
13214
|
+
return g ? `M ${g.x0} ${g.y0} L ${g.x1} ${g.y1}` : null;
|
|
13215
|
+
}, ...(ngDevMode ? [{ debugName: "path" }] : /* istanbul ignore next */ []));
|
|
13216
|
+
constructor(deps) {
|
|
13217
|
+
this.deps = deps;
|
|
13218
|
+
}
|
|
13219
|
+
/** Clear any link selection — the host calls this when the user selects a row instead. */
|
|
13220
|
+
clearSelection() {
|
|
13221
|
+
this.selectedKey.set(null);
|
|
13222
|
+
}
|
|
13223
|
+
/** Toggle the clicked link's selection. */
|
|
13224
|
+
toggleSelection(key) {
|
|
13225
|
+
this.selectedKey.set(this.selectedKey() === key ? null : key);
|
|
13226
|
+
}
|
|
13227
|
+
/**
|
|
13228
|
+
* Start a link gesture from one end of a bar. Which end it started on, plus which end of the
|
|
13229
|
+
* target row it is dropped nearest to, is what selects the dependency type — so all four
|
|
13230
|
+
* MS-Project relationships are reachable with the same single drag.
|
|
13231
|
+
*
|
|
13232
|
+
* Listeners go on `document`, not the connector: the connector is an 8px dot and any real drag
|
|
13233
|
+
* leaves it immediately.
|
|
13234
|
+
*/
|
|
13235
|
+
start(ev, row, anchor) {
|
|
13236
|
+
if (this.deps.readonly() || row.flat.row.readonly || ev.button !== 0)
|
|
13237
|
+
return;
|
|
13238
|
+
ev.preventDefault();
|
|
13239
|
+
ev.stopPropagation();
|
|
13240
|
+
const local = this.toBodyPoint(ev.clientX, ev.clientY);
|
|
13241
|
+
const x0 = row.shape === 'milestone' ? row.pointX : anchor === 'finish' ? row.endX : row.startX;
|
|
13242
|
+
this.gesture.set({
|
|
13243
|
+
fromId: row.flat.row.id,
|
|
13244
|
+
anchor,
|
|
13245
|
+
x0,
|
|
13246
|
+
y0: row.midY,
|
|
13247
|
+
x1: local.x,
|
|
13248
|
+
y1: local.y,
|
|
13249
|
+
});
|
|
13250
|
+
const onMove = (e) => {
|
|
13251
|
+
const p = this.toBodyPoint(e.clientX, e.clientY);
|
|
13252
|
+
const g = this.gesture();
|
|
13253
|
+
if (g)
|
|
13254
|
+
this.gesture.set({ ...g, x1: p.x, y1: p.y });
|
|
13255
|
+
};
|
|
13256
|
+
const onUp = (e) => {
|
|
13257
|
+
document.removeEventListener('pointermove', onMove);
|
|
13258
|
+
document.removeEventListener('pointerup', onUp);
|
|
13259
|
+
const g = this.gesture();
|
|
13260
|
+
this.gesture.set(null);
|
|
13261
|
+
if (!g)
|
|
13262
|
+
return;
|
|
13263
|
+
const target = this.dropTargetAt(e.clientX, e.clientY);
|
|
13264
|
+
// A row cannot depend on itself; a drop on empty space is simply abandoned.
|
|
13265
|
+
if (target && target.id !== g.fromId) {
|
|
13266
|
+
this.deps.createLink({
|
|
13267
|
+
fromId: g.fromId,
|
|
13268
|
+
toId: target.id,
|
|
13269
|
+
type: linkTypeFor(g.anchor, target.anchor),
|
|
13270
|
+
});
|
|
13271
|
+
}
|
|
13272
|
+
};
|
|
13273
|
+
document.addEventListener('pointermove', onMove);
|
|
13274
|
+
document.addEventListener('pointerup', onUp);
|
|
13275
|
+
this.deps.destroyRef.onDestroy(() => {
|
|
13276
|
+
document.removeEventListener('pointermove', onMove);
|
|
13277
|
+
document.removeEventListener('pointerup', onUp);
|
|
13278
|
+
});
|
|
13279
|
+
}
|
|
13280
|
+
/** Convert client coordinates to a point inside the body SVG (render space). */
|
|
13281
|
+
toBodyPoint(clientX, clientY) {
|
|
13282
|
+
const svg = this.deps.bodySvg();
|
|
13283
|
+
if (!svg)
|
|
13284
|
+
return { x: 0, y: 0 };
|
|
13285
|
+
const rect = svg.getBoundingClientRect();
|
|
13286
|
+
return { x: clientX - rect.left, y: clientY - rect.top };
|
|
13287
|
+
}
|
|
13288
|
+
/**
|
|
13289
|
+
* The row a link gesture was dropped on **and which of its ends** the drop landed nearest —
|
|
13290
|
+
* the second half of the type derivation. A milestone has no width, so both of its ends are the
|
|
13291
|
+
* same point and it is reported as `start`, giving the FS/SS pair anyone actually wants.
|
|
13292
|
+
*/
|
|
13293
|
+
dropTargetAt(clientX, clientY) {
|
|
13294
|
+
const el = document.elementFromPoint(clientX, clientY);
|
|
13295
|
+
const holder = el?.closest('[data-row-id]');
|
|
13296
|
+
const id = holder?.getAttribute('data-row-id');
|
|
13297
|
+
if (!id)
|
|
13298
|
+
return null;
|
|
13299
|
+
const row = this.deps.rows().find((r) => r.flat.row.id === id);
|
|
13300
|
+
if (!row || row.shape === 'milestone' || row.shape === 'empty')
|
|
13301
|
+
return { id, anchor: 'start' };
|
|
13302
|
+
const x = this.toBodyPoint(clientX, clientY).x;
|
|
13303
|
+
return {
|
|
13304
|
+
id,
|
|
13305
|
+
anchor: Math.abs(x - row.endX) < Math.abs(x - row.startX) ? 'finish' : 'start',
|
|
13306
|
+
};
|
|
13307
|
+
}
|
|
13308
|
+
}
|
|
13309
|
+
|
|
12871
13310
|
/** Process-wide counter so each instance owns a unique arrowhead `<marker>` id. */
|
|
12872
13311
|
let _flyGanttUid = 0;
|
|
12873
13312
|
/** Header band heights (two-tier: coarse period over fine period). */
|
|
@@ -12878,27 +13317,6 @@ const HEADER_H = HEADER_UPPER_H + HEADER_LOWER_H;
|
|
|
12878
13317
|
const BAR_V_PAD = 6;
|
|
12879
13318
|
/** Edge-handle hit width (px) for resize + the link connector radius. */
|
|
12880
13319
|
const HANDLE_W = 8;
|
|
12881
|
-
/** Label-pane width bounds (px) the splitter clamps to. */
|
|
12882
|
-
const MIN_LABEL_W = 140;
|
|
12883
|
-
const MAX_LABEL_W = 720;
|
|
12884
|
-
/** Keyboard resize steps (plain arrow / with Shift). */
|
|
12885
|
-
const LABEL_STEP = 16;
|
|
12886
|
-
const LABEL_STEP_COARSE = 64;
|
|
12887
|
-
/** Straight run (px) a dependency elbow travels out of a bar edge before it turns. */
|
|
12888
|
-
const LINK_STUB = 12;
|
|
12889
|
-
/** Clamp any candidate label-pane width into the splitter's range. */
|
|
12890
|
-
function clampLabelWidth(px) {
|
|
12891
|
-
return Math.max(MIN_LABEL_W, Math.min(MAX_LABEL_W, Math.round(px)));
|
|
12892
|
-
}
|
|
12893
|
-
/**
|
|
12894
|
-
* Relationship kind implied by the edge a gesture left and the edge it landed on. The name is
|
|
12895
|
-
* literally `<predecessor endpoint><successor endpoint>`, so this is a lookup, not a rule.
|
|
12896
|
-
*/
|
|
12897
|
-
function linkTypeFor(from, to) {
|
|
12898
|
-
if (from === 'finish')
|
|
12899
|
-
return to === 'start' ? 'FS' : 'FF';
|
|
12900
|
-
return to === 'start' ? 'SS' : 'SF';
|
|
12901
|
-
}
|
|
12902
13320
|
/**
|
|
12903
13321
|
* **`fly-gantt`** — the design-system SVG Gantt chart.
|
|
12904
13322
|
*
|
|
@@ -12970,22 +13388,16 @@ class FlyGanttComponent {
|
|
|
12970
13388
|
HEADER_UPPER_H = HEADER_UPPER_H;
|
|
12971
13389
|
HEADER_LOWER_H = HEADER_LOWER_H;
|
|
12972
13390
|
HANDLE_W = HANDLE_W;
|
|
12973
|
-
MIN_LABEL_W =
|
|
12974
|
-
MAX_LABEL_W =
|
|
13391
|
+
MIN_LABEL_W = GANTT_LABEL_W_MIN;
|
|
13392
|
+
MAX_LABEL_W = GANTT_LABEL_W_MAX;
|
|
12975
13393
|
// ── Interaction state ─────────────────────────────────────────────────────────
|
|
12976
13394
|
selectedId = signal(null, ...(ngDevMode ? [{ debugName: "selectedId" }] : /* istanbul ignore next */ []));
|
|
12977
|
-
/** Key (`from~to~type`) of the dependency arrow the user has selected, if any. */
|
|
12978
|
-
selectedLinkKey = signal(null, ...(ngDevMode ? [{ debugName: "selectedLinkKey" }] : /* istanbul ignore next */ []));
|
|
12979
13395
|
_collapsed = signal(new Set(), ...(ngDevMode ? [{ debugName: "_collapsed" }] : /* istanbul ignore next */ []));
|
|
12980
13396
|
/** Live drag preview `{id,start,end}` folded into geometry while a gesture runs. */
|
|
12981
13397
|
_dragPreview = signal(null, ...(ngDevMode ? [{ debugName: "_dragPreview" }] : /* istanbul ignore next */ []));
|
|
12982
|
-
/** Rubber-band link gesture render-space anchor + cursor, and its origin row + edge. */
|
|
12983
|
-
linkGesture = signal(null, ...(ngDevMode ? [{ debugName: "linkGesture" }] : /* istanbul ignore next */ []));
|
|
12984
13398
|
dragTooltip = signal(null, ...(ngDevMode ? [{ debugName: "dragTooltip" }] : /* istanbul ignore next */ []));
|
|
12985
13399
|
/** True while a divider drag is in flight (suppresses text selection host-wide). */
|
|
12986
|
-
resizingLabels = signal(false, ...(ngDevMode ? [{ debugName: "resizingLabels" }] : /* istanbul ignore next */ []));
|
|
12987
13400
|
/** User-chosen pane width; `null` = still following the {@link labelWidth} input. */
|
|
12988
|
-
_labelWidthOverride = signal(null, ...(ngDevMode ? [{ debugName: "_labelWidthOverride" }] : /* istanbul ignore next */ []));
|
|
12989
13401
|
bodyRef = viewChild('bodySvg', ...(ngDevMode ? [{ debugName: "bodyRef" }] : /* istanbul ignore next */ []));
|
|
12990
13402
|
// ── Derived model ──────────────────────────────────────────────────────────────
|
|
12991
13403
|
rtl = computed(() => this.i18n.isRtl(), ...(ngDevMode ? [{ debugName: "rtl" }] : /* istanbul ignore next */ []));
|
|
@@ -12993,7 +13405,6 @@ class FlyGanttComponent {
|
|
|
12993
13405
|
domain = computed(() => computeDomain(this.rows(), this.zoom()), ...(ngDevMode ? [{ debugName: "domain" }] : /* istanbul ignore next */ []));
|
|
12994
13406
|
innerWidth = computed(() => Math.max(1, dateToX(this.domain().end, this.domain().start, this.pxPerDay())), ...(ngDevMode ? [{ debugName: "innerWidth" }] : /* istanbul ignore next */ []));
|
|
12995
13407
|
/** Label-pane width actually rendered — the user's dragged width, else the input. */
|
|
12996
|
-
effectiveLabelWidth = computed(() => clampLabelWidth(this._labelWidthOverride() ?? this.labelWidth()), ...(ngDevMode ? [{ debugName: "effectiveLabelWidth" }] : /* istanbul ignore next */ []));
|
|
12997
13408
|
/** Full visible list after tree flatten/collapse. */
|
|
12998
13409
|
_flat = computed(() => flattenRows(this.rows(), this._collapsed()), ...(ngDevMode ? [{ debugName: "_flat" }] : /* istanbul ignore next */ []));
|
|
12999
13410
|
/** Capped list actually rendered. */
|
|
@@ -13095,7 +13506,7 @@ class FlyGanttComponent {
|
|
|
13095
13506
|
const to = vmById.get(link.toId);
|
|
13096
13507
|
if (!from || !to)
|
|
13097
13508
|
continue;
|
|
13098
|
-
const routed =
|
|
13509
|
+
const routed = routeLink(from, to, link.type, rtl, this.rowHeight());
|
|
13099
13510
|
out.push({
|
|
13100
13511
|
key: `${link.fromId}~${link.toId}~${link.type}`,
|
|
13101
13512
|
fromId: link.fromId,
|
|
@@ -13151,85 +13562,31 @@ class FlyGanttComponent {
|
|
|
13151
13562
|
}
|
|
13152
13563
|
// ── Label-pane divider ─────────────────────────────────────────────────────────
|
|
13153
13564
|
/**
|
|
13154
|
-
*
|
|
13155
|
-
*
|
|
13156
|
-
*
|
|
13565
|
+
* The divider's own state machine, in {@link GanttLabelPane}. It is composed rather than
|
|
13566
|
+
* inlined because it is the one region of this component with no coupling to the chart: give it
|
|
13567
|
+
* a direction, a fallback width and somewhere to report a committed value and it is complete.
|
|
13568
|
+
* The subtle part — that "wider" flips physical sign under RTL for BOTH the drag and the arrow
|
|
13569
|
+
* keys — is stated once there instead of twice here.
|
|
13157
13570
|
*/
|
|
13571
|
+
_labelPane = new GanttLabelPane({
|
|
13572
|
+
resizable: this.resizableLabels,
|
|
13573
|
+
rtl: this.rtl,
|
|
13574
|
+
inputWidth: this.labelWidth,
|
|
13575
|
+
commit: (width) => this.labelWidthChange.emit(width),
|
|
13576
|
+
destroyRef: this.destroyRef,
|
|
13577
|
+
});
|
|
13578
|
+
/** Painted width of the label pane — the user's dragged value, else the clamped input. */
|
|
13579
|
+
effectiveLabelWidth = this._labelPane.width;
|
|
13580
|
+
/** True mid-drag; drives the host's resizing cursor/affordance. */
|
|
13581
|
+
resizingLabels = this._labelPane.resizing;
|
|
13158
13582
|
onLabelResizePointerDown(ev) {
|
|
13159
|
-
|
|
13160
|
-
return;
|
|
13161
|
-
ev.preventDefault();
|
|
13162
|
-
ev.stopPropagation();
|
|
13163
|
-
const startClientX = ev.clientX;
|
|
13164
|
-
const startWidth = this.effectiveLabelWidth();
|
|
13165
|
-
const dirSign = this.rtl() ? -1 : 1;
|
|
13166
|
-
this.resizingLabels.set(true);
|
|
13167
|
-
const onMove = (e) => {
|
|
13168
|
-
this._labelWidthOverride.set(clampLabelWidth(startWidth + (e.clientX - startClientX) * dirSign));
|
|
13169
|
-
};
|
|
13170
|
-
const onUp = () => {
|
|
13171
|
-
document.removeEventListener('pointermove', onMove);
|
|
13172
|
-
document.removeEventListener('pointerup', onUp);
|
|
13173
|
-
this.resizingLabels.set(false);
|
|
13174
|
-
const final = this.effectiveLabelWidth();
|
|
13175
|
-
if (final !== startWidth)
|
|
13176
|
-
this.labelWidthChange.emit(final);
|
|
13177
|
-
};
|
|
13178
|
-
document.addEventListener('pointermove', onMove);
|
|
13179
|
-
document.addEventListener('pointerup', onUp);
|
|
13180
|
-
this.destroyRef.onDestroy(() => {
|
|
13181
|
-
document.removeEventListener('pointermove', onMove);
|
|
13182
|
-
document.removeEventListener('pointerup', onUp);
|
|
13183
|
-
});
|
|
13583
|
+
this._labelPane.onPointerDown(ev);
|
|
13184
13584
|
}
|
|
13185
|
-
/**
|
|
13186
|
-
* Keyboard resize on the focused divider. ArrowRight/Left grow/shrink by the *inline* meaning
|
|
13187
|
-
* of the key, so under RTL the arrow that points away from the pane is still the one that
|
|
13188
|
-
* widens it. `Home`/`End` jump to the bounds, Enter/Escape restore the input width.
|
|
13189
|
-
*
|
|
13190
|
-
* Typed `Event` for the same reason as {@link onLabelRowKeydown}: Angular's strict template
|
|
13191
|
-
* checker types `(keydown)` `$event` as `Event`, so the cast is done here once.
|
|
13192
|
-
*/
|
|
13193
13585
|
onLabelResizeKeydown(ev) {
|
|
13194
|
-
|
|
13195
|
-
return;
|
|
13196
|
-
const key = ev.key;
|
|
13197
|
-
const coarse = ev.shiftKey;
|
|
13198
|
-
const step = coarse ? LABEL_STEP_COARSE : LABEL_STEP;
|
|
13199
|
-
const grow = this.rtl() ? 'ArrowLeft' : 'ArrowRight';
|
|
13200
|
-
const shrink = this.rtl() ? 'ArrowRight' : 'ArrowLeft';
|
|
13201
|
-
let next;
|
|
13202
|
-
if (key === grow)
|
|
13203
|
-
next = this.effectiveLabelWidth() + step;
|
|
13204
|
-
else if (key === shrink)
|
|
13205
|
-
next = this.effectiveLabelWidth() - step;
|
|
13206
|
-
else if (key === 'Home')
|
|
13207
|
-
next = MIN_LABEL_W;
|
|
13208
|
-
else if (key === 'End')
|
|
13209
|
-
next = MAX_LABEL_W;
|
|
13210
|
-
else if (key === 'Enter' || key === 'Escape') {
|
|
13211
|
-
// Stop the grid-level Enter handler from also activating the selected row.
|
|
13212
|
-
ev.preventDefault();
|
|
13213
|
-
ev.stopPropagation();
|
|
13214
|
-
this.resetLabelWidth();
|
|
13215
|
-
return;
|
|
13216
|
-
}
|
|
13217
|
-
else
|
|
13218
|
-
return;
|
|
13219
|
-
ev.preventDefault();
|
|
13220
|
-
ev.stopPropagation();
|
|
13221
|
-
const clamped = clampLabelWidth(next);
|
|
13222
|
-
if (clamped === this.effectiveLabelWidth())
|
|
13223
|
-
return;
|
|
13224
|
-
this._labelWidthOverride.set(clamped);
|
|
13225
|
-
this.labelWidthChange.emit(clamped);
|
|
13586
|
+
this._labelPane.onKeydown(ev);
|
|
13226
13587
|
}
|
|
13227
|
-
/** Drop the user's dragged width and fall back to the {@link labelWidth} input. */
|
|
13228
13588
|
resetLabelWidth() {
|
|
13229
|
-
|
|
13230
|
-
return;
|
|
13231
|
-
this._labelWidthOverride.set(null);
|
|
13232
|
-
this.labelWidthChange.emit(this.effectiveLabelWidth());
|
|
13589
|
+
this._labelPane.reset();
|
|
13233
13590
|
}
|
|
13234
13591
|
// ── Group collapse ─────────────────────────────────────────────────────────────
|
|
13235
13592
|
isCollapsed(id) {
|
|
@@ -13260,7 +13617,7 @@ class FlyGanttComponent {
|
|
|
13260
13617
|
this.selectedId.set(id);
|
|
13261
13618
|
// A row and a link are alternative selections — picking one drops the other, so Delete is
|
|
13262
13619
|
// never ambiguous about which thing it would remove.
|
|
13263
|
-
this.
|
|
13620
|
+
this._links.clearSelection();
|
|
13264
13621
|
this.rowClick.emit(id);
|
|
13265
13622
|
}
|
|
13266
13623
|
onRowDblClick(id) {
|
|
@@ -13269,14 +13626,14 @@ class FlyGanttComponent {
|
|
|
13269
13626
|
// ── Dependency arrow selection + removal ───────────────────────────────────────
|
|
13270
13627
|
onLinkClick(ev, vm) {
|
|
13271
13628
|
ev.stopPropagation();
|
|
13272
|
-
this.
|
|
13629
|
+
this._links.toggleSelection(vm.key);
|
|
13273
13630
|
}
|
|
13274
13631
|
deleteLink(ev, vm) {
|
|
13275
13632
|
ev.stopPropagation();
|
|
13276
13633
|
ev.preventDefault();
|
|
13277
13634
|
if (this.readonly())
|
|
13278
13635
|
return;
|
|
13279
|
-
this.
|
|
13636
|
+
this._links.clearSelection();
|
|
13280
13637
|
this.dependencyDelete.emit({ fromId: vm.fromId, toId: vm.toId, type: vm.type });
|
|
13281
13638
|
}
|
|
13282
13639
|
// ── Keyboard ───────────────────────────────────────────────────────────────────
|
|
@@ -13413,54 +13770,27 @@ class FlyGanttComponent {
|
|
|
13413
13770
|
}
|
|
13414
13771
|
// ── Pointer drag: dependency create ──────────────────────────────────────────────
|
|
13415
13772
|
/**
|
|
13416
|
-
*
|
|
13417
|
-
*
|
|
13418
|
-
*
|
|
13773
|
+
* The dependency-drawing gesture, in {@link GanttLinkGestures}. Composed rather than inlined
|
|
13774
|
+
* because it is a self-contained second gesture whose interesting part is a RULE — the end you
|
|
13775
|
+
* start on plus the end you drop nearest, selects the relationship type — which reads far better beside
|
|
13776
|
+
* its own hit-testing than in the middle of this component.
|
|
13419
13777
|
*/
|
|
13778
|
+
_links = new GanttLinkGestures({
|
|
13779
|
+
readonly: this.readonly,
|
|
13780
|
+
rows: this.rowVms,
|
|
13781
|
+
bodySvg: () => this.bodyRef()?.nativeElement ?? null,
|
|
13782
|
+
createLink: (dependency) => this.dependencyCreate.emit(dependency),
|
|
13783
|
+
destroyRef: this.destroyRef,
|
|
13784
|
+
});
|
|
13785
|
+
/** Key (`from~to~type`) of the dependency arrow the user has selected, if any. */
|
|
13786
|
+
selectedLinkKey = this._links.selectedKey;
|
|
13787
|
+
/** The in-flight rubber-band gesture, or `null` — the template draws it when present. */
|
|
13788
|
+
linkGesture = this._links.gesture;
|
|
13789
|
+
/** Rubber-band path for an in-flight link gesture (render space). */
|
|
13790
|
+
linkGesturePath = this._links.path;
|
|
13420
13791
|
onLinkPointerDown(ev, vm, anchor) {
|
|
13421
|
-
|
|
13422
|
-
return;
|
|
13423
|
-
ev.preventDefault();
|
|
13424
|
-
ev.stopPropagation();
|
|
13425
|
-
const local = this._toBodyPoint(ev);
|
|
13426
|
-
const x0 = vm.shape === 'milestone' ? vm.pointX : anchor === 'finish' ? vm.endX : vm.startX;
|
|
13427
|
-
this.linkGesture.set({ fromId: vm.flat.row.id, anchor, x0, y0: vm.midY, x1: local.x, y1: local.y });
|
|
13428
|
-
const onMove = (e) => {
|
|
13429
|
-
const p = this._toBodyPoint(e);
|
|
13430
|
-
const g = this.linkGesture();
|
|
13431
|
-
if (g)
|
|
13432
|
-
this.linkGesture.set({ ...g, x1: p.x, y1: p.y });
|
|
13433
|
-
};
|
|
13434
|
-
const onUp = (e) => {
|
|
13435
|
-
document.removeEventListener('pointermove', onMove);
|
|
13436
|
-
document.removeEventListener('pointerup', onUp);
|
|
13437
|
-
const g = this.linkGesture();
|
|
13438
|
-
this.linkGesture.set(null);
|
|
13439
|
-
if (!g)
|
|
13440
|
-
return;
|
|
13441
|
-
const target = this._dropTargetAt(e.clientX, e.clientY);
|
|
13442
|
-
if (target && target.id !== g.fromId) {
|
|
13443
|
-
this.dependencyCreate.emit({
|
|
13444
|
-
fromId: g.fromId,
|
|
13445
|
-
toId: target.id,
|
|
13446
|
-
type: linkTypeFor(g.anchor, target.anchor),
|
|
13447
|
-
});
|
|
13448
|
-
}
|
|
13449
|
-
};
|
|
13450
|
-
document.addEventListener('pointermove', onMove);
|
|
13451
|
-
document.addEventListener('pointerup', onUp);
|
|
13452
|
-
this.destroyRef.onDestroy(() => {
|
|
13453
|
-
document.removeEventListener('pointermove', onMove);
|
|
13454
|
-
document.removeEventListener('pointerup', onUp);
|
|
13455
|
-
});
|
|
13792
|
+
this._links.start(ev, vm, anchor);
|
|
13456
13793
|
}
|
|
13457
|
-
/** Rubber-band path for an in-flight link gesture (render space). */
|
|
13458
|
-
linkGesturePath = computed(() => {
|
|
13459
|
-
const g = this.linkGesture();
|
|
13460
|
-
if (!g)
|
|
13461
|
-
return null;
|
|
13462
|
-
return `M ${g.x0} ${g.y0} L ${g.x1} ${g.y1}`;
|
|
13463
|
-
}, ...(ngDevMode ? [{ debugName: "linkGesturePath" }] : /* istanbul ignore next */ []));
|
|
13464
13794
|
// ── Internals ────────────────────────────────────────────────────────────────────
|
|
13465
13795
|
/**
|
|
13466
13796
|
* Resolve a row's `backgroundColor` into the low-alpha band colour actually painted.
|
|
@@ -13481,70 +13811,6 @@ class FlyGanttComponent {
|
|
|
13481
13811
|
// Reflect each cell: its inline-start edge becomes innerWidth - (x + width).
|
|
13482
13812
|
return ticks.map((t) => ({ ...t, x: w - t.x - t.width }));
|
|
13483
13813
|
}
|
|
13484
|
-
/**
|
|
13485
|
-
* Route a dependency elbow for any of the four relationship kinds (render space).
|
|
13486
|
-
*
|
|
13487
|
-
* The kind names the two endpoints, so it decides both the anchor x's and the direction the
|
|
13488
|
-
* arrow travels: leaving a *finish* edge moves forward in time, leaving a *start* edge moves
|
|
13489
|
-
* backward; entering a *start* edge approaches from behind, entering a *finish* edge from
|
|
13490
|
-
* ahead. `fwd` folds RTL in, so none of that branches on direction.
|
|
13491
|
-
*
|
|
13492
|
-
* One turn suffices when the entry stub lies ahead of the exit stub in the approach
|
|
13493
|
-
* direction. When it does not — overlapping or reversed bars, which SS/FF/SF hit constantly —
|
|
13494
|
-
* a straight drop would cut back through the bars, so the elbow detours via the gap between
|
|
13495
|
-
* the two rows instead.
|
|
13496
|
-
*/
|
|
13497
|
-
_route(from, to, type, rtl) {
|
|
13498
|
-
const fwd = rtl ? -1 : 1;
|
|
13499
|
-
const fromFinish = type === 'FS' || type === 'FF';
|
|
13500
|
-
const toStart = type === 'FS' || type === 'SS';
|
|
13501
|
-
const x1 = from.shape === 'milestone' ? from.pointX : fromFinish ? from.endX : from.startX;
|
|
13502
|
-
const x2 = to.shape === 'milestone' ? to.pointX : toStart ? to.startX : to.endX;
|
|
13503
|
-
const y1 = from.midY;
|
|
13504
|
-
const y2 = to.midY;
|
|
13505
|
-
const d1 = (fromFinish ? 1 : -1) * fwd; // exit direction
|
|
13506
|
-
const d2 = (toStart ? 1 : -1) * fwd; // approach direction
|
|
13507
|
-
const exit = x1 + LINK_STUB * d1;
|
|
13508
|
-
const entry = x2 - LINK_STUB * d2;
|
|
13509
|
-
if ((entry - exit) * d2 >= 0) {
|
|
13510
|
-
return { path: `M ${x1} ${y1} H ${exit} V ${y2} H ${x2}`, badgeX: exit, badgeY: (y1 + y2) / 2 };
|
|
13511
|
-
}
|
|
13512
|
-
// Distinct rows always differ in y; the guard only keeps a degenerate input off the bar.
|
|
13513
|
-
const yMid = y1 === y2 ? y1 - this.rowHeight() / 2 : (y1 + y2) / 2;
|
|
13514
|
-
return {
|
|
13515
|
-
path: `M ${x1} ${y1} H ${exit} V ${yMid} H ${entry} V ${y2} H ${x2}`,
|
|
13516
|
-
badgeX: entry,
|
|
13517
|
-
badgeY: yMid,
|
|
13518
|
-
};
|
|
13519
|
-
}
|
|
13520
|
-
/** Convert a pointer event to a coordinate inside the body SVG (render space). */
|
|
13521
|
-
_toBodyPoint(ev) {
|
|
13522
|
-
const svg = this.bodyRef()?.nativeElement;
|
|
13523
|
-
if (!svg)
|
|
13524
|
-
return { x: 0, y: 0 };
|
|
13525
|
-
const rect = svg.getBoundingClientRect();
|
|
13526
|
-
return { x: ev.clientX - rect.left, y: ev.clientY - rect.top };
|
|
13527
|
-
}
|
|
13528
|
-
/**
|
|
13529
|
-
* The row a link gesture was dropped on **and which of its ends** the drop landed nearest —
|
|
13530
|
-
* the second half of the type derivation. A milestone has no width, so both of its ends are
|
|
13531
|
-
* the same point and it is reported as `start`, giving the FS/SS pair anyone actually wants.
|
|
13532
|
-
*/
|
|
13533
|
-
_dropTargetAt(clientX, clientY) {
|
|
13534
|
-
const el = document.elementFromPoint(clientX, clientY);
|
|
13535
|
-
const holder = el?.closest('[data-row-id]');
|
|
13536
|
-
const id = holder?.getAttribute('data-row-id');
|
|
13537
|
-
if (!id)
|
|
13538
|
-
return null;
|
|
13539
|
-
const vm = this.rowVms().find((v) => v.flat.row.id === id);
|
|
13540
|
-
if (!vm || vm.shape === 'milestone' || vm.shape === 'empty')
|
|
13541
|
-
return { id, anchor: 'start' };
|
|
13542
|
-
const x = this._toBodyPoint({ clientX, clientY }).x;
|
|
13543
|
-
return {
|
|
13544
|
-
id,
|
|
13545
|
-
anchor: Math.abs(x - vm.endX) < Math.abs(x - vm.startX) ? 'finish' : 'start',
|
|
13546
|
-
};
|
|
13547
|
-
}
|
|
13548
13814
|
_ariaFor(row, start, end, shape) {
|
|
13549
13815
|
const t = (k, p) => this.i18n.t(k, p);
|
|
13550
13816
|
if (shape === 'milestone') {
|
|
@@ -19370,6 +19636,29 @@ function flyUnwrapCurrencies(res) {
|
|
|
19370
19636
|
const data = res?.data;
|
|
19371
19637
|
return Array.isArray(data) ? data : [];
|
|
19372
19638
|
}
|
|
19639
|
+
/** The platform endpoint serving the CALLING TENANT's default currency. Relative, like its
|
|
19640
|
+
* catalogue sibling — the host's gateway + auth interceptors apply, and the answer is scoped to
|
|
19641
|
+
* whatever tenant that auth resolves to. */
|
|
19642
|
+
const FLY_CURRENCY_DEFAULT_ENDPOINT = '/api/currencies/default';
|
|
19643
|
+
/**
|
|
19644
|
+
* Unwraps the single-row shape of `GET /api/currencies/default`.
|
|
19645
|
+
*
|
|
19646
|
+
* `null` is a SUCCESS answer, not a failure: a tenant with no country has no default currency,
|
|
19647
|
+
* and a platform-scoped caller has no tenant whose currency could be meant. Anything that is not
|
|
19648
|
+
* recognisably a currency row — an envelope with `data: null`, an error body, an HTML login page
|
|
19649
|
+
* from a misrouted request — collapses to `null` for the same reason
|
|
19650
|
+
* {@link flyUnwrapCurrencies} returns `[]`: a picker must degrade to "no preselection", never
|
|
19651
|
+
* preselect something the platform did not vouch for.
|
|
19652
|
+
*/
|
|
19653
|
+
function flyUnwrapCurrency(res) {
|
|
19654
|
+
const candidate = res !== null && typeof res === 'object' && 'data' in res
|
|
19655
|
+
? res.data
|
|
19656
|
+
: res;
|
|
19657
|
+
if (candidate === null || typeof candidate !== 'object')
|
|
19658
|
+
return null;
|
|
19659
|
+
const row = candidate;
|
|
19660
|
+
return typeof row.code === 'string' && row.code.length > 0 ? candidate : null;
|
|
19661
|
+
}
|
|
19373
19662
|
/**
|
|
19374
19663
|
* **`FlyCurrencyCatalogService`** — the one ISO-code → {@link FlyCurrency} adapter for the
|
|
19375
19664
|
* platform currency catalogue (`GET /api/currencies/brief`).
|
|
@@ -19490,6 +19779,8 @@ let _flyCurrencySelectorUid = 0;
|
|
|
19490
19779
|
* Re-exported from {@link FlyCurrencyCatalogService}'s module so the picker and the display-only
|
|
19491
19780
|
* catalogue cannot drift on which route serves the rows. */
|
|
19492
19781
|
const CURRENCIES_BRIEF_ENDPOINT = FLY_CURRENCIES_BRIEF_ENDPOINT;
|
|
19782
|
+
/** The platform endpoint behind {@link FlyCurrencySelectorComponent.applyTenantDefault}. */
|
|
19783
|
+
const CURRENCY_DEFAULT_ENDPOINT = FLY_CURRENCY_DEFAULT_ENDPOINT;
|
|
19493
19784
|
/** Cached answer of {@link probeFlagEmojiSupport} — the measurement is stable for a document's lifetime. */
|
|
19494
19785
|
let _flagEmojiSupport = null;
|
|
19495
19786
|
/**
|
|
@@ -19600,6 +19891,30 @@ class FlyCurrencySelectorComponent {
|
|
|
19600
19891
|
currencies = input(null, ...(ngDevMode ? [{ debugName: "currencies" }] : /* istanbul ignore next */ []));
|
|
19601
19892
|
/** Host-supplied loader, used when `[currencies]` is absent. Falls back to `GET /api/currencies/brief`. */
|
|
19602
19893
|
fetchFn = input(null, ...(ngDevMode ? [{ debugName: "fetchFn" }] : /* istanbul ignore next */ []));
|
|
19894
|
+
/**
|
|
19895
|
+
* Pre-fill an EMPTY single-select with the calling tenant's default currency
|
|
19896
|
+
* (`GET /api/currencies/default`, or `[tenantDefaultFetchFn]`). On by default.
|
|
19897
|
+
*
|
|
19898
|
+
* An amount is nearly always denominated in the tenant's reporting currency, and a form that
|
|
19899
|
+
* opens empty makes every user restate a fact the platform already holds — while an untouched
|
|
19900
|
+
* field silently saves null, which downstream money formatting cannot render at all.
|
|
19901
|
+
*
|
|
19902
|
+
* Deliberately narrow. It applies only when ALL of these hold, because outside them a
|
|
19903
|
+
* preselection would be a guess rather than a default:
|
|
19904
|
+
* - `mode="single"` — a multi-select accumulates a SET, and seeding one member of a set
|
|
19905
|
+
* states something the tenant currency does not say.
|
|
19906
|
+
* - not `locked()` — a frozen value is authoritative; proposing one contradicts the lock.
|
|
19907
|
+
* - the control is still empty — a written value always wins.
|
|
19908
|
+
*
|
|
19909
|
+
* It fires at most once per instance: clearing the field by hand latches it off, so the value
|
|
19910
|
+
* cannot reappear and read as the clear having not registered.
|
|
19911
|
+
*
|
|
19912
|
+
* A host that supplied `[currencies]` or `[fetchFn]` is never taken to the platform endpoint —
|
|
19913
|
+
* see {@link tenantDefaultFetchFn}, which is how such a host opts back in.
|
|
19914
|
+
*/
|
|
19915
|
+
applyTenantDefault = input(true, ...(ngDevMode ? [{ debugName: "applyTenantDefault" }] : /* istanbul ignore next */ []));
|
|
19916
|
+
/** Host-supplied loader for {@link applyTenantDefault}. Falls back to `GET /api/currencies/default`. */
|
|
19917
|
+
tenantDefaultFetchFn = input(null, ...(ngDevMode ? [{ debugName: "tenantDefaultFetchFn" }] : /* istanbul ignore next */ []));
|
|
19603
19918
|
/** Restrict the offered set to these ISO 4217 codes (case-insensitive). Empty = offer everything. */
|
|
19604
19919
|
allowedCodes = input([], ...(ngDevMode ? [{ debugName: "allowedCodes" }] : /* istanbul ignore next */ []));
|
|
19605
19920
|
/**
|
|
@@ -19660,6 +19975,12 @@ class FlyCurrencySelectorComponent {
|
|
|
19660
19975
|
_selectedCodes = signal([], ...(ngDevMode ? [{ debugName: "_selectedCodes" }] : /* istanbul ignore next */ []));
|
|
19661
19976
|
_cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
19662
19977
|
_fetchStarted = false;
|
|
19978
|
+
/** Guards the once-per-instance tenant-default resolve. */
|
|
19979
|
+
_tenantDefaultStarted = false;
|
|
19980
|
+
/** Latches once the default has been applied, or ruled out by any commit (pick, clear, write). */
|
|
19981
|
+
_tenantDefaultSettled = false;
|
|
19982
|
+
/** The resolved row, kept so a `writeValue(null)` arriving AFTER the fetch can still use it. */
|
|
19983
|
+
_tenantDefaultRow = null;
|
|
19663
19984
|
searchEl;
|
|
19664
19985
|
triggerEl;
|
|
19665
19986
|
_onChange = () => undefined;
|
|
@@ -19675,6 +19996,34 @@ class FlyCurrencySelectorComponent {
|
|
|
19675
19996
|
this._fetchStarted = true;
|
|
19676
19997
|
this._load();
|
|
19677
19998
|
});
|
|
19999
|
+
// A control that HOLDS a value must resolve it, whether or not the user ever opens the
|
|
20000
|
+
// panel: `selected()` maps codes through the loaded rows, so a saved code with no rows
|
|
20001
|
+
// renders as the placeholder — an edit form reading "Select a currency" over a project that
|
|
20002
|
+
// is denominated in AED. The lazy rule is still intact for the case it was written for: an
|
|
20003
|
+
// EMPTY selector on a tab nobody opens fetches nothing.
|
|
20004
|
+
effect(() => {
|
|
20005
|
+
if (this._fetchStarted)
|
|
20006
|
+
return;
|
|
20007
|
+
if (this.currencies() !== null)
|
|
20008
|
+
return;
|
|
20009
|
+
if (this._selectedCodes().length === 0)
|
|
20010
|
+
return;
|
|
20011
|
+
this._fetchStarted = true;
|
|
20012
|
+
this._load();
|
|
20013
|
+
});
|
|
20014
|
+
// The tenant default is resolved EAGERLY, unlike the catalogue: its whole job is to be in
|
|
20015
|
+
// place before the user looks at the field, so deferring it to first open would defeat it.
|
|
20016
|
+
// The cost is bounded - one small request per defaulting selector, against an endpoint that
|
|
20017
|
+
// returns a single row. An effect rather than the constructor body so `applyTenantDefault`
|
|
20018
|
+
// and `locked` are read AFTER the host's bindings are applied, not at their declared values.
|
|
20019
|
+
effect(() => {
|
|
20020
|
+
if (this._tenantDefaultStarted)
|
|
20021
|
+
return;
|
|
20022
|
+
if (!this.applyTenantDefault() || this.isMulti() || this.locked())
|
|
20023
|
+
return;
|
|
20024
|
+
this._tenantDefaultStarted = true;
|
|
20025
|
+
this._loadTenantDefault();
|
|
20026
|
+
});
|
|
19678
20027
|
}
|
|
19679
20028
|
// ── Derived view state ─────────────────────────────────────────────────────
|
|
19680
20029
|
effectiveDisabled = computed(() => this.disabled() || this._cvaDisabled(), ...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
|
|
@@ -19899,13 +20248,69 @@ class FlyCurrencySelectorComponent {
|
|
|
19899
20248
|
this._loaded.set(rows);
|
|
19900
20249
|
});
|
|
19901
20250
|
}
|
|
20251
|
+
_loadTenantDefault() {
|
|
20252
|
+
const fetch = this.tenantDefaultFetchFn();
|
|
20253
|
+
// A host that took over the CATALOGUE owns the default too: `[currencies]`/`[fetchFn]` are
|
|
20254
|
+
// the "do not reach the platform routes" declaration (offline builds, fixtures, apps that
|
|
20255
|
+
// proxy reference data), and honouring it for the rows while quietly firing a platform
|
|
20256
|
+
// request for the default would break exactly the consumers those inputs exist for. Such a
|
|
20257
|
+
// host opts back in by supplying `[tenantDefaultFetchFn]`.
|
|
20258
|
+
const hostOwnsData = this.currencies() !== null || this.fetchFn() !== null;
|
|
20259
|
+
const source = fetch
|
|
20260
|
+
? fetch()
|
|
20261
|
+
: this.http && !hostOwnsData
|
|
20262
|
+
? this.http.get(CURRENCY_DEFAULT_ENDPOINT).pipe(map(flyUnwrapCurrency))
|
|
20263
|
+
: null;
|
|
20264
|
+
// No transport and no loader is not an error state here: an offline surface simply has no
|
|
20265
|
+
// default. Nothing is surfaced to the user - unlike a failed CATALOGUE load, which must be
|
|
20266
|
+
// retryable because it leaves the control unusable.
|
|
20267
|
+
if (!source)
|
|
20268
|
+
return;
|
|
20269
|
+
source.pipe(catchError$1(() => of(null))).subscribe((row) => {
|
|
20270
|
+
this._tenantDefaultRow = row;
|
|
20271
|
+
this._applyTenantDefaultIfEmpty();
|
|
20272
|
+
});
|
|
20273
|
+
}
|
|
20274
|
+
/**
|
|
20275
|
+
* Applies the resolved default to a still-empty control.
|
|
20276
|
+
*
|
|
20277
|
+
* Called from both ends of a race with no fixed winner: the fetch may land before the forms
|
|
20278
|
+
* layer writes, or after it writes the empty value a form is built with.
|
|
20279
|
+
*/
|
|
20280
|
+
_applyTenantDefaultIfEmpty() {
|
|
20281
|
+
if (this._tenantDefaultSettled)
|
|
20282
|
+
return;
|
|
20283
|
+
if (!this.applyTenantDefault() || this.isMulti() || this.locked())
|
|
20284
|
+
return;
|
|
20285
|
+
const row = this._tenantDefaultRow;
|
|
20286
|
+
if (!row)
|
|
20287
|
+
return;
|
|
20288
|
+
if (this._selectedCodes().length > 0)
|
|
20289
|
+
return;
|
|
20290
|
+
// Seed the row so the trigger can RENDER the pick straight away. Without this the selection
|
|
20291
|
+
// would be invisible until the catalogue loads - which is lazy, on first open - so the field
|
|
20292
|
+
// would look untouched while its form value said otherwise. `_load` replaces the whole set
|
|
20293
|
+
// on success, and leaves this seed alone on failure.
|
|
20294
|
+
if (this.currencies() === null && this._loaded().length === 0)
|
|
20295
|
+
this._loaded.set([row]);
|
|
20296
|
+
this._commit([row.code]);
|
|
20297
|
+
}
|
|
19902
20298
|
// ── ControlValueAccessor ───────────────────────────────────────────────────
|
|
19903
20299
|
writeValue(value) {
|
|
19904
20300
|
if (value === null || value === undefined) {
|
|
19905
20301
|
this._selectedCodes.set([]);
|
|
20302
|
+
// An empty write re-tries the default - this is the common `patchValue`-after-load path,
|
|
20303
|
+
// and on a fresh form it is the write that arrives BEFORE the fetch has landed.
|
|
20304
|
+
this._applyTenantDefaultIfEmpty();
|
|
19906
20305
|
return;
|
|
19907
20306
|
}
|
|
19908
|
-
|
|
20307
|
+
const codes = typeof value === 'string' ? [value] : [...value];
|
|
20308
|
+
this._selectedCodes.set(codes);
|
|
20309
|
+
// A written value is the host's own answer; there is no hole left for a default to fill.
|
|
20310
|
+
if (codes.length > 0)
|
|
20311
|
+
this._tenantDefaultSettled = true;
|
|
20312
|
+
else
|
|
20313
|
+
this._applyTenantDefaultIfEmpty();
|
|
19909
20314
|
}
|
|
19910
20315
|
registerOnChange(fn) {
|
|
19911
20316
|
this._onChange = fn;
|
|
@@ -19922,6 +20327,10 @@ class FlyCurrencySelectorComponent {
|
|
|
19922
20327
|
return this.isMulti() ? [...codes] : (codes[0] ?? null);
|
|
19923
20328
|
}
|
|
19924
20329
|
_commit(codes) {
|
|
20330
|
+
// Every user-driven change routes through here - pick, toggle, remove, clear - so this is the
|
|
20331
|
+
// one place that has to latch the default off. A cleared field that refilled itself would be
|
|
20332
|
+
// indistinguishable from the clear never having registered.
|
|
20333
|
+
this._tenantDefaultSettled = true;
|
|
19925
20334
|
this._selectedCodes.set([...codes]);
|
|
19926
20335
|
this._onChange(this._controlValue());
|
|
19927
20336
|
this.selectionChange.emit([...codes]);
|
|
@@ -19931,7 +20340,7 @@ class FlyCurrencySelectorComponent {
|
|
|
19931
20340
|
this._onTouched();
|
|
19932
20341
|
}
|
|
19933
20342
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlyCurrencySelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
19934
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlyCurrencySelectorComponent, isStandalone: true, selector: "fly-currency-selector", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, currencies: { classPropertyName: "currencies", publicName: "currencies", isSignal: true, isRequired: false, transformFunction: null }, fetchFn: { classPropertyName: "fetchFn", publicName: "fetchFn", isSignal: true, isRequired: false, transformFunction: null }, allowedCodes: { classPropertyName: "allowedCodes", publicName: "allowedCodes", isSignal: true, isRequired: false, transformFunction: null }, pinnedCodes: { classPropertyName: "pinnedCodes", publicName: "pinnedCodes", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, locked: { classPropertyName: "locked", publicName: "locked", isSignal: true, isRequired: false, transformFunction: null }, lockedReasonKey: { classPropertyName: "lockedReasonKey", publicName: "lockedReasonKey", isSignal: true, isRequired: false, transformFunction: null }, lockedReasonParams: { classPropertyName: "lockedReasonParams", publicName: "lockedReasonParams", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", selectionDetailChange: "selectionDetailChange", openedChange: "openedChange" }, host: { properties: { "class.fly-currency-selector--open": "isOpen()", "class.fly-currency-selector--disabled": "effectiveDisabled()", "class.fly-currency-selector--locked": "locked()" }, classAttribute: "fly-currency-selector" }, providers: [
|
|
20343
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlyCurrencySelectorComponent, isStandalone: true, selector: "fly-currency-selector", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, currencies: { classPropertyName: "currencies", publicName: "currencies", isSignal: true, isRequired: false, transformFunction: null }, fetchFn: { classPropertyName: "fetchFn", publicName: "fetchFn", isSignal: true, isRequired: false, transformFunction: null }, applyTenantDefault: { classPropertyName: "applyTenantDefault", publicName: "applyTenantDefault", isSignal: true, isRequired: false, transformFunction: null }, tenantDefaultFetchFn: { classPropertyName: "tenantDefaultFetchFn", publicName: "tenantDefaultFetchFn", isSignal: true, isRequired: false, transformFunction: null }, allowedCodes: { classPropertyName: "allowedCodes", publicName: "allowedCodes", isSignal: true, isRequired: false, transformFunction: null }, pinnedCodes: { classPropertyName: "pinnedCodes", publicName: "pinnedCodes", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, locked: { classPropertyName: "locked", publicName: "locked", isSignal: true, isRequired: false, transformFunction: null }, lockedReasonKey: { classPropertyName: "lockedReasonKey", publicName: "lockedReasonKey", isSignal: true, isRequired: false, transformFunction: null }, lockedReasonParams: { classPropertyName: "lockedReasonParams", publicName: "lockedReasonParams", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", selectionDetailChange: "selectionDetailChange", openedChange: "openedChange" }, host: { properties: { "class.fly-currency-selector--open": "isOpen()", "class.fly-currency-selector--disabled": "effectiveDisabled()", "class.fly-currency-selector--locked": "locked()" }, classAttribute: "fly-currency-selector" }, providers: [
|
|
19935
20344
|
{
|
|
19936
20345
|
provide: NG_VALUE_ACCESSOR,
|
|
19937
20346
|
useExisting: forwardRef(() => FlyCurrencySelectorComponent),
|
|
@@ -19953,7 +20362,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
19953
20362
|
'[class.fly-currency-selector--disabled]': 'effectiveDisabled()',
|
|
19954
20363
|
'[class.fly-currency-selector--locked]': 'locked()',
|
|
19955
20364
|
}, template: "<div\r\n class=\"fly-currency-selector__wrap\"\r\n [flyClickOutsideEnabled]=\"isOpen()\"\r\n (flyClickOutside)=\"close()\"\r\n>\r\n @if (locked()) {\r\n <!-- \u2500\u2500 Locked readout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n NOT a dimmed `disabled` trigger: there is no dropdown affordance here at all \u2014 the\r\n value is frozen on purpose, not merely unavailable right now. `role=\"group\"` +\r\n `aria-label` name the field the way the combobox trigger's `aria-label` does; the\r\n reason is REAL, always-rendered text (not a `title` attribute, which many screen\r\n readers never announce and which no sighted user sees without hovering) and is\r\n additionally wired via `aria-describedby` for AT that reads by relationship rather\r\n than document order. -->\r\n <div\r\n class=\"fly-currency-selector__locked\"\r\n role=\"group\"\r\n [attr.aria-label]=\"ariaLabel() || placeholderText()\"\r\n [attr.aria-describedby]=\"lockedReasonId\"\r\n >\r\n @if (selected().length === 0) {\r\n <span class=\"fly-currency-selector__placeholder\">{{ placeholderText() }}</span>\r\n } @else {\r\n <span class=\"fly-currency-selector__chips\">\r\n @for (c of selected(); track c.code) {\r\n <span class=\"fly-currency-selector__chip\">\r\n <span class=\"fly-currency-selector__tile\" aria-hidden=\"true\">\r\n @if (flagsRenderable() && c.flag) {\r\n <span class=\"fly-currency-selector__tile-flag\">{{ c.flag }}</span>\r\n } @else {\r\n <span class=\"fly-currency-selector__tile-code\">{{ tileCode(c) }}</span>\r\n }\r\n </span>\r\n <span class=\"fly-currency-selector__chip-code\">{{ c.code }}</span>\r\n <span class=\"fly-currency-selector__chip-symbol\" aria-hidden=\"true\">{{ c.symbol }}</span>\r\n </span>\r\n }\r\n </span>\r\n }\r\n <span class=\"fly-currency-selector__lock-icon\" aria-hidden=\"true\">🔒</span>\r\n </div>\r\n <p class=\"fly-currency-selector__locked-reason\" [id]=\"lockedReasonId\">{{ lockedReasonText() }}</p>\r\n } @else {\r\n\r\n <!-- \u2500\u2500 Trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n `aria-label` is never null: a `combobox` does NOT take its accessible name from its\r\n contents the way a plain button does, so the chips/placeholder painted inside it name\r\n nothing. Absent a consumer label the localized placeholder supplies the control's\r\n purpose; its VALUE is still announced from the contents. -->\r\n <button\r\n #triggerRef\r\n type=\"button\"\r\n [id]=\"triggerId\"\r\n class=\"fly-currency-selector__trigger\"\r\n role=\"combobox\"\r\n aria-haspopup=\"listbox\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-controls]=\"listboxId\"\r\n [attr.aria-label]=\"ariaLabel() || placeholderText()\"\r\n [class.fly-currency-selector__trigger--empty]=\"!hasSelection()\"\r\n [disabled]=\"effectiveDisabled()\"\r\n (click)=\"toggleOpen()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n >\r\n @if (selected().length === 0) {\r\n <span class=\"fly-currency-selector__placeholder\">{{ placeholderText() }}</span>\r\n } @else {\r\n <span class=\"fly-currency-selector__chips\">\r\n @for (c of selected(); track c.code) {\r\n <span class=\"fly-currency-selector__chip\">\r\n <span class=\"fly-currency-selector__tile\" aria-hidden=\"true\">\r\n @if (flagsRenderable() && c.flag) {\r\n <span class=\"fly-currency-selector__tile-flag\">{{ c.flag }}</span>\r\n } @else {\r\n <span class=\"fly-currency-selector__tile-code\">{{ tileCode(c) }}</span>\r\n }\r\n </span>\r\n <span class=\"fly-currency-selector__chip-code\">{{ c.code }}</span>\r\n <span class=\"fly-currency-selector__chip-symbol\" aria-hidden=\"true\">{{ c.symbol }}</span>\r\n @if (isMulti() && !effectiveDisabled()) {\r\n <span\r\n class=\"fly-currency-selector__chip-remove\"\r\n role=\"button\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"'\u2715 ' + c.code\"\r\n (click)=\"remove(c.code, $event)\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n >\u2715</span>\r\n }\r\n </span>\r\n }\r\n </span>\r\n }\r\n\r\n @if (clearable() && hasSelection() && !effectiveDisabled()) {\r\n <span\r\n class=\"fly-currency-selector__clear\"\r\n role=\"button\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"clearText()\"\r\n [title]=\"clearText()\"\r\n (click)=\"clear($event)\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n >\u2715</span>\r\n }\r\n\r\n <span class=\"fly-currency-selector__chevron\" aria-hidden=\"true\">\u25BE</span>\r\n </button>\r\n\r\n <!-- \u2500\u2500 Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n @if (isOpen()) {\r\n <div class=\"fly-currency-selector__panel\" tabindex=\"-1\" (keydown)=\"onPanelKeydown($event)\">\r\n <div class=\"fly-currency-selector__search\">\r\n <input\r\n #searchRef\r\n type=\"text\"\r\n class=\"fly-currency-selector__search-input\"\r\n role=\"combobox\"\r\n aria-autocomplete=\"list\"\r\n [attr.aria-expanded]=\"true\"\r\n [attr.aria-controls]=\"listboxId\"\r\n [attr.aria-activedescendant]=\"activeDescendant() || null\"\r\n [attr.aria-label]=\"searchPlaceholderText()\"\r\n [placeholder]=\"searchPlaceholderText()\"\r\n [value]=\"searchTerm()\"\r\n (input)=\"onSearchInput($any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n @if (loading()) {\r\n <div class=\"fly-currency-selector__status\" role=\"status\">{{ loadingText() }}</div>\r\n } @else if (loadFailed()) {\r\n <div class=\"fly-currency-selector__status fly-currency-selector__status--error\" role=\"alert\">\r\n <span>{{ errorText() }}</span>\r\n <button type=\"button\" class=\"fly-currency-selector__retry\" (click)=\"retry()\">\r\n {{ retryText() }}\r\n </button>\r\n </div>\r\n }\r\n\r\n <div\r\n class=\"fly-currency-selector__listbox\"\r\n role=\"listbox\"\r\n [id]=\"listboxId\"\r\n [attr.aria-multiselectable]=\"isMulti() ? true : null\"\r\n [attr.aria-activedescendant]=\"activeDescendant() || null\"\r\n [attr.aria-label]=\"ariaLabel() || null\"\r\n >\r\n @if (pinned().length > 0) {\r\n <div class=\"fly-currency-selector__group\" role=\"presentation\">{{ pinnedGroupText() }}</div>\r\n @for (c of pinned(); track c.code) {\r\n <ng-container *ngTemplateOutlet=\"row; context: { $implicit: c }\" />\r\n }\r\n @if (rest().length > 0) {\r\n <div class=\"fly-currency-selector__group\" role=\"presentation\">{{ allGroupText() }}</div>\r\n }\r\n }\r\n @for (c of rest(); track c.code) {\r\n <ng-container *ngTemplateOutlet=\"row; context: { $implicit: c }\" />\r\n }\r\n\r\n @if (!loading() && !loadFailed() && filtered().length === 0) {\r\n <div class=\"fly-currency-selector__status\" role=\"presentation\">{{ noResultsText() }}</div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n</div>\r\n\r\n<!-- \u2500\u2500 One option row, shared by the pinned and full groups \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n<ng-template #row let-c>\r\n <div\r\n class=\"fly-currency-selector__option\"\r\n role=\"option\"\r\n tabindex=\"-1\"\r\n [id]=\"optionId(navIndexOf(c.code))\"\r\n [attr.aria-selected]=\"isSelected(c.code)\"\r\n [class.fly-currency-selector__option--selected]=\"isSelected(c.code)\"\r\n [class.fly-currency-selector__option--active]=\"navIndexOf(c.code) === activeIndex()\"\r\n (click)=\"pick(c)\"\r\n (keydown.enter)=\"pick(c)\"\r\n (mouseenter)=\"onOptionHover(navIndexOf(c.code))\"\r\n >\r\n <span class=\"fly-currency-selector__tile\" aria-hidden=\"true\">\r\n @if (flagsRenderable() && c.flag) {\r\n <span class=\"fly-currency-selector__tile-flag\">{{ c.flag }}</span>\r\n } @else {\r\n <span class=\"fly-currency-selector__tile-code\">{{ tileCode(c) }}</span>\r\n }\r\n </span>\r\n\r\n <span class=\"fly-currency-selector__option-text\">\r\n <span class=\"fly-currency-selector__option-code\">{{ c.code }}</span>\r\n <span class=\"fly-currency-selector__option-name\">{{ c.name }}</span>\r\n </span>\r\n\r\n <span class=\"fly-currency-selector__option-symbol\" aria-hidden=\"true\">{{ c.symbol }}</span>\r\n\r\n <span class=\"fly-currency-selector__check\" aria-hidden=\"true\">\r\n @if (isSelected(c.code)) { \u2713 }\r\n </span>\r\n </div>\r\n</ng-template>\r\n", styles: [":host,:host *,:host *:before,:host *:after{box-sizing:border-box}:host{--_surface: var(--surface-card, #fff);--_surface-panel: var(--glass-bg-elevated, var(--surface-card, #fff));--_panel-blur: var(--glass-blur, 40px);--_surface-hover: var(--surface-hover, #f1f5f9);--_surface-active: var(--surface-active, #eef2ff);--_border: var(--surface-border, #e2e8f0);--_text: var(--text-color, #0f172a);--_text-subtle: var(--text-color-secondary, #64748b);--_fill: var(--fill-tertiary, #f3f4f6);--_primary: var(--primary-color);--_danger: var(--system-red, #ef4444);--_radius: 8px;display:inline-block;inline-size:100%;min-inline-size:11rem;font-size:13px;color:var(--_text)}.fly-currency-selector__wrap{position:relative;inline-size:100%}.fly-currency-selector__trigger{display:flex;align-items:center;gap:8px;inline-size:100%;min-block-size:2.25rem;padding-block:4px;padding-inline:8px;border:1px solid var(--_border);border-radius:var(--_radius);background:var(--_surface);color:var(--_text);font:inherit;text-align:start;cursor:pointer}.fly-currency-selector__trigger:focus-visible{outline:2px solid var(--_primary);outline-offset:2px}.fly-currency-selector__trigger:disabled{opacity:.55;cursor:not-allowed}.fly-currency-selector__placeholder{flex:1 1 auto;padding-inline-start:4px;color:var(--_text-subtle);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.fly-currency-selector__chips{display:flex;flex:1 1 auto;flex-wrap:wrap;gap:4px;min-inline-size:0}.fly-currency-selector__chip{display:inline-flex;align-items:center;gap:5px;padding-block:2px;padding-inline:3px 7px;border-radius:999px;background:var(--_fill);font-size:12px}.fly-currency-selector__chip-code{font-weight:700;letter-spacing:.02em}.fly-currency-selector__chip-symbol{color:var(--_text-subtle)}.fly-currency-selector__chip-remove{padding:1px 3px;border-radius:50%;color:var(--_text-subtle);font-size:10px;line-height:1;cursor:pointer}.fly-currency-selector__chip-remove:hover{background:color-mix(in srgb,var(--_danger) 18%,transparent);color:var(--_danger)}.fly-currency-selector__clear,.fly-currency-selector__chevron{flex:none;color:var(--_text-subtle);font-size:11px;line-height:1}.fly-currency-selector__clear{padding:3px;border-radius:50%;cursor:pointer}.fly-currency-selector__clear:hover{background:var(--_surface-hover);color:var(--_text)}.fly-currency-selector__tile{display:inline-flex;flex:none;align-items:center;justify-content:center;inline-size:26px;block-size:19px;border-radius:4px;background:var(--_fill);overflow:hidden}.fly-currency-selector__tile-flag{font-size:15px;line-height:1}.fly-currency-selector__tile-code{font-size:9px;font-weight:800;letter-spacing:.04em;color:var(--_text-subtle);direction:ltr;unicode-bidi:isolate}.fly-currency-selector__locked{display:flex;align-items:center;gap:8px;inline-size:100%;min-block-size:2.25rem;padding-block:4px;padding-inline:8px;border:1px dashed var(--_border);border-radius:var(--_radius);background:var(--_fill);color:var(--_text);cursor:default}.fly-currency-selector__lock-icon{flex:none;margin-inline-start:auto;font-size:12px;opacity:.7}.fly-currency-selector__locked-reason{margin:4px 0 0;color:var(--_text-subtle);font-size:11px;line-height:1.4}.fly-currency-selector__panel{position:absolute;z-index:60;inset-inline:0;inset-block-start:calc(100% + 4px);display:flex;flex-direction:column;max-block-size:19rem;border:1px solid var(--_border);border-radius:var(--_radius);background:var(--_surface-panel);-webkit-backdrop-filter:blur(var(--_panel-blur));backdrop-filter:blur(var(--_panel-blur));box-shadow:0 12px 32px #0000002e;overflow:hidden}.fly-currency-selector__search{padding:6px;border-block-end:1px solid var(--_border)}.fly-currency-selector__search-input{inline-size:100%;padding-block:5px;padding-inline:8px;border:1px solid var(--_border);border-radius:6px;background:var(--_surface);color:var(--_text);font:inherit}.fly-currency-selector__search-input:focus-visible{outline:2px solid var(--_primary);outline-offset:-1px}.fly-currency-selector__listbox{flex:1 1 auto;overflow-y:auto;padding-block:4px}.fly-currency-selector__group{padding-block:6px 3px;padding-inline:10px;color:var(--_text-subtle);font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}.fly-currency-selector__option{display:flex;align-items:center;gap:9px;padding-block:5px;padding-inline:10px;cursor:pointer}.fly-currency-selector__option--active{background:var(--_surface-hover)}.fly-currency-selector__option--selected{background:var(--_surface-active)}.fly-currency-selector__option-text{display:flex;flex:1 1 auto;align-items:baseline;gap:8px;min-inline-size:0}.fly-currency-selector__option-code{flex:none;font-weight:700;letter-spacing:.02em;direction:ltr;unicode-bidi:isolate}.fly-currency-selector__option-name{flex:1 1 auto;min-inline-size:0;color:var(--_text-subtle);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.fly-currency-selector__option-symbol{flex:none;min-inline-size:2.2em;color:var(--_text);text-align:end}.fly-currency-selector__check{flex:none;inline-size:1em;color:var(--_primary)}.fly-currency-selector__status{display:flex;align-items:center;gap:8px;padding-block:10px;padding-inline:10px;color:var(--_text-subtle)}.fly-currency-selector__status--error{color:var(--_danger)}.fly-currency-selector__retry{border:1px solid var(--_border);border-radius:6px;background:var(--_surface);padding-block:2px;padding-inline:8px;color:var(--_text);font:inherit;font-weight:600;cursor:pointer}\n"] }]
|
|
19956
|
-
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], currencies: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencies", required: false }] }], fetchFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "fetchFn", required: false }] }], allowedCodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowedCodes", required: false }] }], pinnedCodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedCodes", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], locked: [{ type: i0.Input, args: [{ isSignal: true, alias: "locked", required: false }] }], lockedReasonKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockedReasonKey", required: false }] }], lockedReasonParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockedReasonParams", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], selectionDetailChange: [{ type: i0.Output, args: ["selectionDetailChange"] }], openedChange: [{ type: i0.Output, args: ["openedChange"] }], searchEl: [{
|
|
20365
|
+
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], currencies: [{ type: i0.Input, args: [{ isSignal: true, alias: "currencies", required: false }] }], fetchFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "fetchFn", required: false }] }], applyTenantDefault: [{ type: i0.Input, args: [{ isSignal: true, alias: "applyTenantDefault", required: false }] }], tenantDefaultFetchFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "tenantDefaultFetchFn", required: false }] }], allowedCodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowedCodes", required: false }] }], pinnedCodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedCodes", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], locked: [{ type: i0.Input, args: [{ isSignal: true, alias: "locked", required: false }] }], lockedReasonKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockedReasonKey", required: false }] }], lockedReasonParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockedReasonParams", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], selectionDetailChange: [{ type: i0.Output, args: ["selectionDetailChange"] }], openedChange: [{ type: i0.Output, args: ["openedChange"] }], searchEl: [{
|
|
19957
20366
|
type: ViewChild,
|
|
19958
20367
|
args: ['searchRef']
|
|
19959
20368
|
}], triggerEl: [{
|
|
@@ -21811,14 +22220,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
21811
22220
|
args: [{ selector: 'fly-meta', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<dl>\r\n @for (item of items(); track $index) {\r\n <div>\r\n <dt>{{ item.labelKey | translate }}</dt>\r\n <dd [class.mono]=\"item.mono ?? false\">{{ item.value }}</dd>\r\n </div>\r\n }\r\n <ng-content />\r\n</dl>\r\n", styles: [":host{display:block}dl{margin:0;display:flex;flex-direction:column;gap:10px}dl ::ng-deep>div{display:grid;grid-template-columns:96px 1fr;gap:var(--sp-3);align-items:baseline;font-size:var(--text-sm)}dl ::ng-deep dt{margin:0;font-size:10.5px;font-weight:var(--fw-medium);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--ink-4)}dl ::ng-deep dd{margin:0;min-width:0;color:var(--ink);word-break:break-word;line-height:1.45}dl ::ng-deep dd.mono,dl ::ng-deep dd .mono{font-size:var(--text-2xs)}\n"] }]
|
|
21812
22221
|
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }] } });
|
|
21813
22222
|
|
|
21814
|
-
function canGoPrev(page) {
|
|
22223
|
+
function canGoPrev$1(page) {
|
|
21815
22224
|
return page > 1;
|
|
21816
22225
|
}
|
|
21817
|
-
function canGoNext(page, totalPages) {
|
|
22226
|
+
function canGoNext$1(page, totalPages) {
|
|
21818
22227
|
return page < totalPages;
|
|
21819
22228
|
}
|
|
21820
22229
|
/** Clamp a requested page into `[1, max(1, totalPages)]`. */
|
|
21821
|
-
function clampPage(page, totalPages) {
|
|
22230
|
+
function clampPage$1(page, totalPages) {
|
|
21822
22231
|
return Math.min(Math.max(1, page), Math.max(1, totalPages));
|
|
21823
22232
|
}
|
|
21824
22233
|
|
|
@@ -21853,10 +22262,10 @@ class FlyPaginationComponent {
|
|
|
21853
22262
|
showEdges = input(false, ...(ngDevMode ? [{ debugName: "showEdges" }] : /* istanbul ignore next */ []));
|
|
21854
22263
|
/** Emits the target page (already clamped to bounds). */
|
|
21855
22264
|
pageChange = output();
|
|
21856
|
-
prevEnabled = computed(() => canGoPrev(this.page()), ...(ngDevMode ? [{ debugName: "prevEnabled" }] : /* istanbul ignore next */ []));
|
|
21857
|
-
nextEnabled = computed(() => canGoNext(this.page(), this.totalPages()), ...(ngDevMode ? [{ debugName: "nextEnabled" }] : /* istanbul ignore next */ []));
|
|
22265
|
+
prevEnabled = computed(() => canGoPrev$1(this.page()), ...(ngDevMode ? [{ debugName: "prevEnabled" }] : /* istanbul ignore next */ []));
|
|
22266
|
+
nextEnabled = computed(() => canGoNext$1(this.page(), this.totalPages()), ...(ngDevMode ? [{ debugName: "nextEnabled" }] : /* istanbul ignore next */ []));
|
|
21858
22267
|
goTo(target) {
|
|
21859
|
-
const next = clampPage(target, this.totalPages());
|
|
22268
|
+
const next = clampPage$1(target, this.totalPages());
|
|
21860
22269
|
if (next !== this.page())
|
|
21861
22270
|
this.pageChange.emit(next);
|
|
21862
22271
|
}
|
|
@@ -21868,6 +22277,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
21868
22277
|
args: [{ selector: 'fly-pagination', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"pg\" [attr.aria-label]=\"'ui.pagination.label' | translate\">\r\n @if (rangeText(); as range) {\r\n <span class=\"pg__range\">{{ range }}</span>\r\n }\r\n <div class=\"pg__controls\">\r\n @if (showEdges()) {\r\n <button\r\n type=\"button\"\r\n class=\"pg__btn\"\r\n [disabled]=\"!prevEnabled()\"\r\n (click)=\"goTo(1)\"\r\n [attr.aria-label]=\"'ui.pagination.first' | translate\"\r\n ><span class=\"pg__chev\" aria-hidden=\"true\">\u00AB</span></button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"pg__btn\"\r\n [disabled]=\"!prevEnabled()\"\r\n (click)=\"goTo(page() - 1)\"\r\n [attr.aria-label]=\"'ui.pagination.previous' | translate\"\r\n >\r\n <svg class=\"pg__arrow\" data-arrow=\"prev\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <polyline points=\"15 6 9 12 15 18\" />\r\n </svg>\r\n </button>\r\n <span class=\"pg__current\" aria-live=\"polite\">\r\n {{ 'ui.pagination.pageOf' | translate: { page: page(), total: totalPages() } }}\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"pg__btn\"\r\n [disabled]=\"!nextEnabled()\"\r\n (click)=\"goTo(page() + 1)\"\r\n [attr.aria-label]=\"'ui.pagination.next' | translate\"\r\n >\r\n <svg class=\"pg__arrow\" data-arrow=\"next\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <polyline points=\"9 6 15 12 9 18\" />\r\n </svg>\r\n </button>\r\n @if (showEdges()) {\r\n <button\r\n type=\"button\"\r\n class=\"pg__btn\"\r\n [disabled]=\"!nextEnabled()\"\r\n (click)=\"goTo(totalPages())\"\r\n [attr.aria-label]=\"'ui.pagination.last' | translate\"\r\n ><span class=\"pg__chev\" aria-hidden=\"true\">\u00BB</span></button>\r\n }\r\n </div>\r\n</nav>\r\n", styles: [".pg{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);margin-block-start:var(--sp-4);font-size:var(--text-sm);color:var(--ink-3)}.pg__range{font-size:var(--text-sm);color:var(--ink-3);direction:ltr;unicode-bidi:isolate}.pg__range b{color:var(--ink);font-weight:var(--fw-medium)}.pg__controls{display:flex;align-items:center;gap:2px;margin-inline-start:auto;font-size:var(--text-sm)}.pg__btn{inline-size:26px;block-size:26px;padding:0;border:1px solid transparent;background:transparent;color:var(--ink-3);border-radius:50%;font-size:var(--text-sm);cursor:pointer;display:inline-grid;place-items:center;transition:background var(--t-state),color var(--t-state)}.pg__btn:hover:not(:disabled){background:var(--w06);color:var(--ink)}.pg__btn:disabled{color:var(--w2);cursor:default}.pg__btn:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}.pg__arrow{inline-size:14px;block-size:14px}:host-context([dir=rtl]) .pg__arrow{transform:scaleX(-1)}.pg__current{font-size:var(--text-sm);font-weight:var(--fw-semibold);font-variant-numeric:tabular-nums;padding:0 var(--sp-3);background:var(--accent);border:1px solid transparent;border-radius:999px;color:var(--on-accent-fill);height:24px;display:inline-grid;place-items:center;direction:ltr;unicode-bidi:isolate}:host-context(.rtl) .pg__chev,:host-context([dir=rtl]) .pg__chev{display:inline-block;transform:scaleX(-1)}\n"] }]
|
|
21869
22278
|
}], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: true }] }], totalPages: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalPages", required: true }] }], rangeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeText", required: false }] }], showEdges: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEdges", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
|
|
21870
22279
|
|
|
22280
|
+
/** 0-indexed sibling of `pagination.logic.ts` — see `pager.component.ts` for why. */
|
|
22281
|
+
function canGoPrev(page) {
|
|
22282
|
+
return page > 0;
|
|
22283
|
+
}
|
|
22284
|
+
function canGoNext(page, totalPages) {
|
|
22285
|
+
return page < totalPages - 1;
|
|
22286
|
+
}
|
|
22287
|
+
/** Clamp a requested page into `[0, max(0, totalPages - 1)]`. */
|
|
22288
|
+
function clampPage(page, totalPages) {
|
|
22289
|
+
return Math.min(Math.max(0, page), Math.max(0, totalPages - 1));
|
|
22290
|
+
}
|
|
22291
|
+
|
|
22292
|
+
/**
|
|
22293
|
+
* Numbered list pager — back arrow, one pill per page, forward arrow — for the
|
|
22294
|
+
* lists that want every page as a target rather than `fly-pagination`'s
|
|
22295
|
+
* «prev / page of total / next» band. Shares the list's surface and continues
|
|
22296
|
+
* its border (see `COMPONENTS.md`): pair with a list container that sets
|
|
22297
|
+
* `border-bottom: 0` so the two read as one panel.
|
|
22298
|
+
*
|
|
22299
|
+
* <fly-pager [page]="page()" [totalPages]="totalPages()" (goToPage)="goTo($event)" />
|
|
22300
|
+
*
|
|
22301
|
+
* `page`/the emitted target are 0-indexed (array-index pagination); buttons
|
|
22302
|
+
* display `page + 1`. This is the opposite convention from `fly-pagination`
|
|
22303
|
+
* (1-indexed) — the two are separate components for separate list shapes, not
|
|
22304
|
+
* a shared base, so pick the one matching how the caller already indexes its
|
|
22305
|
+
* pages rather than converting at the call site.
|
|
22306
|
+
*/
|
|
22307
|
+
class FlyPagerComponent {
|
|
22308
|
+
page = input.required(...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
|
|
22309
|
+
totalPages = input.required(...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
|
|
22310
|
+
/** Emits the target page (already clamped to bounds), 0-indexed. */
|
|
22311
|
+
goToPage = output();
|
|
22312
|
+
prevEnabled = computed(() => canGoPrev(this.page()), ...(ngDevMode ? [{ debugName: "prevEnabled" }] : /* istanbul ignore next */ []));
|
|
22313
|
+
nextEnabled = computed(() => canGoNext(this.page(), this.totalPages()), ...(ngDevMode ? [{ debugName: "nextEnabled" }] : /* istanbul ignore next */ []));
|
|
22314
|
+
pages = computed(() => Array.from({ length: Math.max(0, this.totalPages()) }, (_, i) => i), ...(ngDevMode ? [{ debugName: "pages" }] : /* istanbul ignore next */ []));
|
|
22315
|
+
goTo(target) {
|
|
22316
|
+
const next = clampPage(target, this.totalPages());
|
|
22317
|
+
if (next !== this.page())
|
|
22318
|
+
this.goToPage.emit(next);
|
|
22319
|
+
}
|
|
22320
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlyPagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
22321
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlyPagerComponent, isStandalone: true, selector: "fly-pager", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: true, transformFunction: null }, totalPages: { classPropertyName: "totalPages", publicName: "totalPages", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { goToPage: "goToPage" }, ngImport: i0, template: "@if (totalPages() > 1) {\r\n <nav class=\"pgr\" [attr.aria-label]=\"'ui.pagination.label' | translate\">\r\n <button\r\n type=\"button\"\r\n class=\"pgr__arrow\"\r\n data-arrow=\"back\"\r\n [disabled]=\"!prevEnabled()\"\r\n (click)=\"goTo(page() - 1)\"\r\n [attr.aria-label]=\"'ui.pager.previous' | translate\"\r\n >\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M14.8 5.2L8 12l6.8 6.8\" />\r\n </svg>\r\n </button>\r\n @for (p of pages(); track p) {\r\n <button\r\n type=\"button\"\r\n class=\"pgr__page\"\r\n [class.pgr__page--current]=\"p === page()\"\r\n [attr.aria-current]=\"p === page() ? 'page' : null\"\r\n (click)=\"goTo(p)\"\r\n [attr.aria-label]=\"'ui.pager.page' | translate: { page: p + 1 }\"\r\n >{{ p + 1 }}</button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"pgr__arrow\"\r\n data-arrow=\"forward\"\r\n [disabled]=\"!nextEnabled()\"\r\n (click)=\"goTo(page() + 1)\"\r\n [attr.aria-label]=\"'ui.pager.next' | translate\"\r\n >\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M9.2 5.2L16 12l-6.8 6.8\" />\r\n </svg>\r\n </button>\r\n </nav>\r\n}\r\n", styles: [".pgr{flex:none;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:4px;padding:14px 8px;background:var(--w03);border:1px solid var(--w08);border-top:0}.pgr__arrow{flex:none;inline-size:26px;block-size:26px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--w6);cursor:pointer;display:grid;place-items:center;transition:background var(--t-state),color var(--t-state)}.pgr__arrow svg{inline-size:14px;block-size:14px}.pgr__arrow:hover:not(:disabled){background:var(--w06)}.pgr__arrow:disabled{color:var(--w2);cursor:default}.pgr__arrow:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}:host-context([dir=rtl]) .pgr__arrow[data-arrow] svg{transform:scaleX(-1)}.pgr__page{flex:none;min-inline-size:24px;block-size:24px;padding:0 3px;border:0;border-radius:50%;background:transparent;color:var(--w45);cursor:pointer;font-family:inherit;font-size:12px;font-weight:600;line-height:1;font-variant-numeric:tabular-nums;transition:background var(--t-state),color var(--t-state)}.pgr__page:hover{background:var(--w08)}.pgr__page--current{background:var(--accent);color:var(--on-accent-fill)}.pgr__page--current:hover{background:var(--accent)}.pgr__page:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
22322
|
+
}
|
|
22323
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlyPagerComponent, decorators: [{
|
|
22324
|
+
type: Component,
|
|
22325
|
+
args: [{ selector: 'fly-pager', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (totalPages() > 1) {\r\n <nav class=\"pgr\" [attr.aria-label]=\"'ui.pagination.label' | translate\">\r\n <button\r\n type=\"button\"\r\n class=\"pgr__arrow\"\r\n data-arrow=\"back\"\r\n [disabled]=\"!prevEnabled()\"\r\n (click)=\"goTo(page() - 1)\"\r\n [attr.aria-label]=\"'ui.pager.previous' | translate\"\r\n >\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M14.8 5.2L8 12l6.8 6.8\" />\r\n </svg>\r\n </button>\r\n @for (p of pages(); track p) {\r\n <button\r\n type=\"button\"\r\n class=\"pgr__page\"\r\n [class.pgr__page--current]=\"p === page()\"\r\n [attr.aria-current]=\"p === page() ? 'page' : null\"\r\n (click)=\"goTo(p)\"\r\n [attr.aria-label]=\"'ui.pager.page' | translate: { page: p + 1 }\"\r\n >{{ p + 1 }}</button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"pgr__arrow\"\r\n data-arrow=\"forward\"\r\n [disabled]=\"!nextEnabled()\"\r\n (click)=\"goTo(page() + 1)\"\r\n [attr.aria-label]=\"'ui.pager.next' | translate\"\r\n >\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\r\n <path d=\"M9.2 5.2L16 12l-6.8 6.8\" />\r\n </svg>\r\n </button>\r\n </nav>\r\n}\r\n", styles: [".pgr{flex:none;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:4px;padding:14px 8px;background:var(--w03);border:1px solid var(--w08);border-top:0}.pgr__arrow{flex:none;inline-size:26px;block-size:26px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--w6);cursor:pointer;display:grid;place-items:center;transition:background var(--t-state),color var(--t-state)}.pgr__arrow svg{inline-size:14px;block-size:14px}.pgr__arrow:hover:not(:disabled){background:var(--w06)}.pgr__arrow:disabled{color:var(--w2);cursor:default}.pgr__arrow:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}:host-context([dir=rtl]) .pgr__arrow[data-arrow] svg{transform:scaleX(-1)}.pgr__page{flex:none;min-inline-size:24px;block-size:24px;padding:0 3px;border:0;border-radius:50%;background:transparent;color:var(--w45);cursor:pointer;font-family:inherit;font-size:12px;font-weight:600;line-height:1;font-variant-numeric:tabular-nums;transition:background var(--t-state),color var(--t-state)}.pgr__page:hover{background:var(--w08)}.pgr__page--current{background:var(--accent);color:var(--on-accent-fill)}.pgr__page--current:hover{background:var(--accent)}.pgr__page:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}\n"] }]
|
|
22326
|
+
}], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: true }] }], totalPages: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalPages", required: true }] }], goToPage: [{ type: i0.Output, args: ["goToPage"] }] } });
|
|
22327
|
+
|
|
21871
22328
|
/**
|
|
21872
22329
|
* Listing card shell — one anatomy for the three reference card patterns:
|
|
21873
22330
|
*
|
|
@@ -22828,11 +23285,11 @@ class PageHeaderComponent {
|
|
|
22828
23285
|
return '#';
|
|
22829
23286
|
}
|
|
22830
23287
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: PageHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
22831
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: PageHeaderComponent, isStandalone: true, selector: "fly-page-header", inputs: { variant: "variant", title: "title", titleKey: "titleKey", subtitle: "subtitle", subtitleKey: "subtitleKey", titleAr: "titleAr", eyebrow: "eyebrow", eyebrowKey: "eyebrowKey", metricValue: "metricValue", metricLabel: "metricLabel", metricLabelKey: "metricLabelKey", breadcrumbs: "breadcrumbs" }, outputs: { crumbClick: "crumbClick" }, ngImport: i0, template: "<header class=\"ph\" [ngClass]=\"'ph--' + variant\">\r\n <!-- \u2500\u2500\u2500 Breadcrumbs \u2500\u2500\u2500 -->\r\n @if (breadcrumbs?.length) {\r\n <nav class=\"crumbs\" [attr.aria-label]=\"'ui.nav.breadcrumbs' | translate\">\r\n @for (bc of breadcrumbs; track $index; let i = $index, last = $last) {\r\n @if (i > 0) {\r\n <span class=\"crumb__sep\" aria-hidden=\"true\">/</span>\r\n }\r\n @if (last || bc.ariaCurrent) {\r\n <span class=\"crumb crumb--active\" aria-current=\"page\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </span>\r\n } @else if (bc.onClick) {\r\n <button type=\"button\" class=\"crumb crumb--link\" (click)=\"onCrumb(bc)\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </button>\r\n } @else if (bc.link) {\r\n <a class=\"crumb crumb--link\" (click)=\"onCrumb(bc); $event.preventDefault()\" [attr.href]=\"hrefFor(bc)\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </a>\r\n } @else if (bc.href) {\r\n <a class=\"crumb crumb--link\" [attr.href]=\"bc.href\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </a>\r\n } @else {\r\n <span class=\"crumb\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </span>\r\n }\r\n }\r\n </nav>\r\n }\r\n\r\n <!-- \u2500\u2500\u2500 Title row \u2500\u2500\u2500 -->\r\n <div class=\"ph__row\">\r\n <div class=\"ph__l\">\r\n @if (eyebrow || eyebrowKey) {\r\n <div class=\"ph__eyebrow\">\r\n @if (eyebrow) { <span class=\"mono\">{{ eyebrow }}</span> }\r\n @if (eyebrowKey) { <span>{{ eyebrowKey | translate }}</span> }\r\n </div>\r\n }\r\n <h1 class=\"ph__title\" [ngClass]=\"{ 'ph__title--detail': variant === 'detail' }\">\r\n {{ titleKey ? (titleKey | translate) : title }}\r\n </h1>\r\n @if (subtitle || subtitleKey) {\r\n <p class=\"ph__sub\">{{ subtitleKey ? (subtitleKey | translate) : subtitle }}</p>\r\n }\r\n @if (titleAr) {\r\n <p class=\"ph__title-ar\">{{ titleAr }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"ph__r\">\r\n <!-- Right-side metric (listing) -->\r\n @if (metricValue || metricLabel || metricLabelKey) {\r\n <div class=\"ph__metric\">\r\n @if (metricValue) { <div class=\"ph__metric-v mono\">{{ metricValue }}</div> }\r\n <div class=\"ph__metric-l\">{{ metricLabelKey ? (metricLabelKey | translate) : metricLabel }}</div>\r\n </div>\r\n }\r\n\r\n <!-- Status chip (detail) stacked over the action button row. Listing/circles/module\r\n project no status, so this collapses to just the right-aligned action row.\r\n NOTE: a single [page-header-actions] slot \u2014 duplicating it across @if branches\r\n silently drops projected content in the non-first branch (Angular binds projection\r\n to the first declared matching <ng-content>). -->\r\n <div class=\"ph__hd-actions\">\r\n <ng-content select=\"[page-header-status]\"></ng-content>\r\n <div class=\"ph__hd-btns\">\r\n <ng-content select=\"[page-header-actions]\"></ng-content>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Default slot: anything the consumer wants directly below the title row\r\n (e.g. a detail-page stat bar). -->\r\n <ng-content></ng-content>\r\n</header>\r\n", styles: ["@charset \"UTF-8\";:host{display:block;margin-block-end:var(--sp-6);container-type:inline-size}.ph{display:flex;flex-direction:column;gap:0;color:var(--ink);font-family:var(--font-sans, var(--font-family, system-ui, sans-serif))}.crumbs{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;margin-bottom:14px;color:var(--ink-3)}.crumb{font-size:12px;line-height:1.4;color:var(--ink-3);padding:2px 0;background:none;border:0;font-family:inherit;letter-spacing:-.005em;text-decoration:none}.crumb--link{cursor:pointer;padding-inline:4px;margin-inline:-4px;border-radius:6px;transition:color var(--t-state),background var(--t-state)}.crumb--link:hover{color:var(--ink);background:var(--w08)}.crumb--active{color:var(--ink);font-weight:var(--fw-medium);max-width:480px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.crumb__sep{color:var(--ink-4);font-size:11px;-webkit-user-select:none;user-select:none}.ph__row{display:flex;align-items:flex-end;justify-content:space-between;gap:12px 32px;flex-wrap:wrap}.ph--detail .ph__row{align-items:flex-start}.ph__l{flex:1}.ph__r{display:flex;align-items:flex-end;gap:16px;flex-shrink:0}.ph--detail .ph__r{align-items:flex-end;flex-direction:column}.ph__eyebrow{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-4);font-weight:var(--fw-medium);margin-bottom:4px}.ph__eyebrow .mono{font-size:10.5px;color:var(--ink-3);letter-spacing:.04em;font-family:var(--font-mono, ui-monospace, monospace)}.ph__title{margin:0 0 6px;font-size:32px;font-weight:var(--fw-semibold);letter-spacing:-.02em;line-height:1.1;color:var(--ink)}.ph__title--detail{font-size:28px;letter-spacing:-.02em;line-height:1.2;margin:0}.ph__sub{margin:0;font-size:13.5px;color:var(--ink-3)}.ph__title-ar{margin:6px 0 0;font-size:17px;color:var(--ink-3);
|
|
23288
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: PageHeaderComponent, isStandalone: true, selector: "fly-page-header", inputs: { variant: "variant", title: "title", titleKey: "titleKey", subtitle: "subtitle", subtitleKey: "subtitleKey", titleAr: "titleAr", eyebrow: "eyebrow", eyebrowKey: "eyebrowKey", metricValue: "metricValue", metricLabel: "metricLabel", metricLabelKey: "metricLabelKey", breadcrumbs: "breadcrumbs" }, outputs: { crumbClick: "crumbClick" }, ngImport: i0, template: "<header class=\"ph\" [ngClass]=\"'ph--' + variant\">\r\n <!-- \u2500\u2500\u2500 Breadcrumbs \u2500\u2500\u2500 -->\r\n @if (breadcrumbs?.length) {\r\n <nav class=\"crumbs\" [attr.aria-label]=\"'ui.nav.breadcrumbs' | translate\">\r\n @for (bc of breadcrumbs; track $index; let i = $index, last = $last) {\r\n @if (i > 0) {\r\n <span class=\"crumb__sep\" aria-hidden=\"true\">/</span>\r\n }\r\n @if (last || bc.ariaCurrent) {\r\n <span class=\"crumb crumb--active\" aria-current=\"page\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </span>\r\n } @else if (bc.onClick) {\r\n <button type=\"button\" class=\"crumb crumb--link\" (click)=\"onCrumb(bc)\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </button>\r\n } @else if (bc.link) {\r\n <a class=\"crumb crumb--link\" (click)=\"onCrumb(bc); $event.preventDefault()\" [attr.href]=\"hrefFor(bc)\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </a>\r\n } @else if (bc.href) {\r\n <a class=\"crumb crumb--link\" [attr.href]=\"bc.href\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </a>\r\n } @else {\r\n <span class=\"crumb\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </span>\r\n }\r\n }\r\n </nav>\r\n }\r\n\r\n <!-- \u2500\u2500\u2500 Title row \u2500\u2500\u2500 -->\r\n <div class=\"ph__row\">\r\n <div class=\"ph__l\">\r\n @if (eyebrow || eyebrowKey) {\r\n <div class=\"ph__eyebrow\">\r\n @if (eyebrow) { <span class=\"mono\">{{ eyebrow }}</span> }\r\n @if (eyebrowKey) { <span>{{ eyebrowKey | translate }}</span> }\r\n </div>\r\n }\r\n <h1 class=\"ph__title\" [ngClass]=\"{ 'ph__title--detail': variant === 'detail' }\">\r\n {{ titleKey ? (titleKey | translate) : title }}\r\n </h1>\r\n @if (subtitle || subtitleKey) {\r\n <p class=\"ph__sub\">{{ subtitleKey ? (subtitleKey | translate) : subtitle }}</p>\r\n }\r\n @if (titleAr) {\r\n <p class=\"ph__title-ar\">{{ titleAr }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"ph__r\">\r\n <!-- Right-side metric (listing) -->\r\n @if (metricValue || metricLabel || metricLabelKey) {\r\n <div class=\"ph__metric\">\r\n @if (metricValue) { <div class=\"ph__metric-v mono\">{{ metricValue }}</div> }\r\n <div class=\"ph__metric-l\">{{ metricLabelKey ? (metricLabelKey | translate) : metricLabel }}</div>\r\n </div>\r\n }\r\n\r\n <!-- Status chip (detail) stacked over the action button row. Listing/circles/module\r\n project no status, so this collapses to just the right-aligned action row.\r\n NOTE: a single [page-header-actions] slot \u2014 duplicating it across @if branches\r\n silently drops projected content in the non-first branch (Angular binds projection\r\n to the first declared matching <ng-content>). -->\r\n <div class=\"ph__hd-actions\">\r\n <ng-content select=\"[page-header-status]\"></ng-content>\r\n <div class=\"ph__hd-btns\">\r\n <ng-content select=\"[page-header-actions]\"></ng-content>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Default slot: anything the consumer wants directly below the title row\r\n (e.g. a detail-page stat bar). -->\r\n <ng-content></ng-content>\r\n</header>\r\n", styles: ["@charset \"UTF-8\";:host{display:block;margin-block-end:var(--sp-6);container-type:inline-size}.ph{display:flex;flex-direction:column;gap:0;color:var(--ink);font-family:var(--font-sans, var(--font-family, system-ui, sans-serif))}.crumbs{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;margin-bottom:14px;color:var(--ink-3)}.crumb{font-size:12px;line-height:1.4;color:var(--ink-3);padding:2px 0;background:none;border:0;font-family:inherit;letter-spacing:-.005em;text-decoration:none}.crumb--link{cursor:pointer;padding-inline:4px;margin-inline:-4px;border-radius:6px;transition:color var(--t-state),background var(--t-state)}.crumb--link:hover{color:var(--ink);background:var(--w08)}.crumb--active{color:var(--ink);font-weight:var(--fw-medium);max-width:480px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.crumb__sep{color:var(--ink-4);font-size:11px;-webkit-user-select:none;user-select:none}.ph__row{display:flex;align-items:flex-end;justify-content:space-between;gap:12px 32px;flex-wrap:wrap}.ph--detail .ph__row{align-items:flex-start}.ph__l{flex:1}.ph__r{display:flex;align-items:flex-end;gap:16px;flex-shrink:0}.ph--detail .ph__r{align-items:flex-end;flex-direction:column}.ph__eyebrow{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-4);font-weight:var(--fw-medium);margin-bottom:4px}.ph__eyebrow .mono{font-size:10.5px;color:var(--ink-3);letter-spacing:.04em;font-family:var(--font-mono, ui-monospace, monospace)}.ph__title{margin:0 0 6px;font-size:32px;font-weight:var(--fw-semibold);letter-spacing:-.02em;line-height:1.1;color:var(--ink)}.ph__title--detail{font-size:28px;letter-spacing:-.02em;line-height:1.2;margin:0}.ph__sub{margin:0;font-size:13.5px;color:var(--ink-3)}.ph__title-ar{margin:6px 0 0;font-size:17px;color:var(--ink-3);letter-spacing:0;line-height:1.5;font-weight:var(--fw-medium)}.ph__metric{text-align:end}.ph__metric-v{font-family:var(--font-mono, ui-monospace, monospace);font-variant-numeric:tabular-nums;font-size:22px;font-weight:var(--fw-medium);letter-spacing:-.02em;color:var(--ink)}.ph__metric-l{font-size:11px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.06em}.ph__hd-actions{display:flex;flex-direction:column;align-items:flex-end;gap:10px}.ph__hd-btns{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}.ph--listing,.ph--detail{margin-bottom:0}@container (width <= 640px){.ph__row{flex-direction:column;align-items:flex-start;gap:16px}.ph__r{align-items:flex-start;flex-flow:row wrap}.ph--detail .ph__r{flex-direction:row-reverse}.ph__title{font-size:26px}.ph__title--detail{font-size:24px}}@media(width<=640px){.ph__row{flex-direction:column;align-items:flex-start;gap:16px}.ph__r{align-items:flex-start;flex-flow:row wrap}.ph--detail .ph__r{flex-direction:row-reverse}.ph__title{font-size:26px}.ph__title--detail{font-size:24px}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
22832
23289
|
}
|
|
22833
23290
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: PageHeaderComponent, decorators: [{
|
|
22834
23291
|
type: Component,
|
|
22835
|
-
args: [{ selector: 'fly-page-header', standalone: true, imports: [NgClass, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"ph\" [ngClass]=\"'ph--' + variant\">\r\n <!-- \u2500\u2500\u2500 Breadcrumbs \u2500\u2500\u2500 -->\r\n @if (breadcrumbs?.length) {\r\n <nav class=\"crumbs\" [attr.aria-label]=\"'ui.nav.breadcrumbs' | translate\">\r\n @for (bc of breadcrumbs; track $index; let i = $index, last = $last) {\r\n @if (i > 0) {\r\n <span class=\"crumb__sep\" aria-hidden=\"true\">/</span>\r\n }\r\n @if (last || bc.ariaCurrent) {\r\n <span class=\"crumb crumb--active\" aria-current=\"page\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </span>\r\n } @else if (bc.onClick) {\r\n <button type=\"button\" class=\"crumb crumb--link\" (click)=\"onCrumb(bc)\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </button>\r\n } @else if (bc.link) {\r\n <a class=\"crumb crumb--link\" (click)=\"onCrumb(bc); $event.preventDefault()\" [attr.href]=\"hrefFor(bc)\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </a>\r\n } @else if (bc.href) {\r\n <a class=\"crumb crumb--link\" [attr.href]=\"bc.href\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </a>\r\n } @else {\r\n <span class=\"crumb\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </span>\r\n }\r\n }\r\n </nav>\r\n }\r\n\r\n <!-- \u2500\u2500\u2500 Title row \u2500\u2500\u2500 -->\r\n <div class=\"ph__row\">\r\n <div class=\"ph__l\">\r\n @if (eyebrow || eyebrowKey) {\r\n <div class=\"ph__eyebrow\">\r\n @if (eyebrow) { <span class=\"mono\">{{ eyebrow }}</span> }\r\n @if (eyebrowKey) { <span>{{ eyebrowKey | translate }}</span> }\r\n </div>\r\n }\r\n <h1 class=\"ph__title\" [ngClass]=\"{ 'ph__title--detail': variant === 'detail' }\">\r\n {{ titleKey ? (titleKey | translate) : title }}\r\n </h1>\r\n @if (subtitle || subtitleKey) {\r\n <p class=\"ph__sub\">{{ subtitleKey ? (subtitleKey | translate) : subtitle }}</p>\r\n }\r\n @if (titleAr) {\r\n <p class=\"ph__title-ar\">{{ titleAr }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"ph__r\">\r\n <!-- Right-side metric (listing) -->\r\n @if (metricValue || metricLabel || metricLabelKey) {\r\n <div class=\"ph__metric\">\r\n @if (metricValue) { <div class=\"ph__metric-v mono\">{{ metricValue }}</div> }\r\n <div class=\"ph__metric-l\">{{ metricLabelKey ? (metricLabelKey | translate) : metricLabel }}</div>\r\n </div>\r\n }\r\n\r\n <!-- Status chip (detail) stacked over the action button row. Listing/circles/module\r\n project no status, so this collapses to just the right-aligned action row.\r\n NOTE: a single [page-header-actions] slot \u2014 duplicating it across @if branches\r\n silently drops projected content in the non-first branch (Angular binds projection\r\n to the first declared matching <ng-content>). -->\r\n <div class=\"ph__hd-actions\">\r\n <ng-content select=\"[page-header-status]\"></ng-content>\r\n <div class=\"ph__hd-btns\">\r\n <ng-content select=\"[page-header-actions]\"></ng-content>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Default slot: anything the consumer wants directly below the title row\r\n (e.g. a detail-page stat bar). -->\r\n <ng-content></ng-content>\r\n</header>\r\n", styles: ["@charset \"UTF-8\";:host{display:block;margin-block-end:var(--sp-6);container-type:inline-size}.ph{display:flex;flex-direction:column;gap:0;color:var(--ink);font-family:var(--font-sans, var(--font-family, system-ui, sans-serif))}.crumbs{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;margin-bottom:14px;color:var(--ink-3)}.crumb{font-size:12px;line-height:1.4;color:var(--ink-3);padding:2px 0;background:none;border:0;font-family:inherit;letter-spacing:-.005em;text-decoration:none}.crumb--link{cursor:pointer;padding-inline:4px;margin-inline:-4px;border-radius:6px;transition:color var(--t-state),background var(--t-state)}.crumb--link:hover{color:var(--ink);background:var(--w08)}.crumb--active{color:var(--ink);font-weight:var(--fw-medium);max-width:480px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.crumb__sep{color:var(--ink-4);font-size:11px;-webkit-user-select:none;user-select:none}.ph__row{display:flex;align-items:flex-end;justify-content:space-between;gap:12px 32px;flex-wrap:wrap}.ph--detail .ph__row{align-items:flex-start}.ph__l{flex:1}.ph__r{display:flex;align-items:flex-end;gap:16px;flex-shrink:0}.ph--detail .ph__r{align-items:flex-end;flex-direction:column}.ph__eyebrow{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-4);font-weight:var(--fw-medium);margin-bottom:4px}.ph__eyebrow .mono{font-size:10.5px;color:var(--ink-3);letter-spacing:.04em;font-family:var(--font-mono, ui-monospace, monospace)}.ph__title{margin:0 0 6px;font-size:32px;font-weight:var(--fw-semibold);letter-spacing:-.02em;line-height:1.1;color:var(--ink)}.ph__title--detail{font-size:28px;letter-spacing:-.02em;line-height:1.2;margin:0}.ph__sub{margin:0;font-size:13.5px;color:var(--ink-3)}.ph__title-ar{margin:6px 0 0;font-size:17px;color:var(--ink-3);
|
|
23292
|
+
args: [{ selector: 'fly-page-header', standalone: true, imports: [NgClass, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"ph\" [ngClass]=\"'ph--' + variant\">\r\n <!-- \u2500\u2500\u2500 Breadcrumbs \u2500\u2500\u2500 -->\r\n @if (breadcrumbs?.length) {\r\n <nav class=\"crumbs\" [attr.aria-label]=\"'ui.nav.breadcrumbs' | translate\">\r\n @for (bc of breadcrumbs; track $index; let i = $index, last = $last) {\r\n @if (i > 0) {\r\n <span class=\"crumb__sep\" aria-hidden=\"true\">/</span>\r\n }\r\n @if (last || bc.ariaCurrent) {\r\n <span class=\"crumb crumb--active\" aria-current=\"page\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </span>\r\n } @else if (bc.onClick) {\r\n <button type=\"button\" class=\"crumb crumb--link\" (click)=\"onCrumb(bc)\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </button>\r\n } @else if (bc.link) {\r\n <a class=\"crumb crumb--link\" (click)=\"onCrumb(bc); $event.preventDefault()\" [attr.href]=\"hrefFor(bc)\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </a>\r\n } @else if (bc.href) {\r\n <a class=\"crumb crumb--link\" [attr.href]=\"bc.href\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </a>\r\n } @else {\r\n <span class=\"crumb\">\r\n {{ bc.labelKey ? (bc.labelKey | translate) : bc.label }}\r\n </span>\r\n }\r\n }\r\n </nav>\r\n }\r\n\r\n <!-- \u2500\u2500\u2500 Title row \u2500\u2500\u2500 -->\r\n <div class=\"ph__row\">\r\n <div class=\"ph__l\">\r\n @if (eyebrow || eyebrowKey) {\r\n <div class=\"ph__eyebrow\">\r\n @if (eyebrow) { <span class=\"mono\">{{ eyebrow }}</span> }\r\n @if (eyebrowKey) { <span>{{ eyebrowKey | translate }}</span> }\r\n </div>\r\n }\r\n <h1 class=\"ph__title\" [ngClass]=\"{ 'ph__title--detail': variant === 'detail' }\">\r\n {{ titleKey ? (titleKey | translate) : title }}\r\n </h1>\r\n @if (subtitle || subtitleKey) {\r\n <p class=\"ph__sub\">{{ subtitleKey ? (subtitleKey | translate) : subtitle }}</p>\r\n }\r\n @if (titleAr) {\r\n <p class=\"ph__title-ar\">{{ titleAr }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"ph__r\">\r\n <!-- Right-side metric (listing) -->\r\n @if (metricValue || metricLabel || metricLabelKey) {\r\n <div class=\"ph__metric\">\r\n @if (metricValue) { <div class=\"ph__metric-v mono\">{{ metricValue }}</div> }\r\n <div class=\"ph__metric-l\">{{ metricLabelKey ? (metricLabelKey | translate) : metricLabel }}</div>\r\n </div>\r\n }\r\n\r\n <!-- Status chip (detail) stacked over the action button row. Listing/circles/module\r\n project no status, so this collapses to just the right-aligned action row.\r\n NOTE: a single [page-header-actions] slot \u2014 duplicating it across @if branches\r\n silently drops projected content in the non-first branch (Angular binds projection\r\n to the first declared matching <ng-content>). -->\r\n <div class=\"ph__hd-actions\">\r\n <ng-content select=\"[page-header-status]\"></ng-content>\r\n <div class=\"ph__hd-btns\">\r\n <ng-content select=\"[page-header-actions]\"></ng-content>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Default slot: anything the consumer wants directly below the title row\r\n (e.g. a detail-page stat bar). -->\r\n <ng-content></ng-content>\r\n</header>\r\n", styles: ["@charset \"UTF-8\";:host{display:block;margin-block-end:var(--sp-6);container-type:inline-size}.ph{display:flex;flex-direction:column;gap:0;color:var(--ink);font-family:var(--font-sans, var(--font-family, system-ui, sans-serif))}.crumbs{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;margin-bottom:14px;color:var(--ink-3)}.crumb{font-size:12px;line-height:1.4;color:var(--ink-3);padding:2px 0;background:none;border:0;font-family:inherit;letter-spacing:-.005em;text-decoration:none}.crumb--link{cursor:pointer;padding-inline:4px;margin-inline:-4px;border-radius:6px;transition:color var(--t-state),background var(--t-state)}.crumb--link:hover{color:var(--ink);background:var(--w08)}.crumb--active{color:var(--ink);font-weight:var(--fw-medium);max-width:480px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.crumb__sep{color:var(--ink-4);font-size:11px;-webkit-user-select:none;user-select:none}.ph__row{display:flex;align-items:flex-end;justify-content:space-between;gap:12px 32px;flex-wrap:wrap}.ph--detail .ph__row{align-items:flex-start}.ph__l{flex:1}.ph__r{display:flex;align-items:flex-end;gap:16px;flex-shrink:0}.ph--detail .ph__r{align-items:flex-end;flex-direction:column}.ph__eyebrow{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-4);font-weight:var(--fw-medium);margin-bottom:4px}.ph__eyebrow .mono{font-size:10.5px;color:var(--ink-3);letter-spacing:.04em;font-family:var(--font-mono, ui-monospace, monospace)}.ph__title{margin:0 0 6px;font-size:32px;font-weight:var(--fw-semibold);letter-spacing:-.02em;line-height:1.1;color:var(--ink)}.ph__title--detail{font-size:28px;letter-spacing:-.02em;line-height:1.2;margin:0}.ph__sub{margin:0;font-size:13.5px;color:var(--ink-3)}.ph__title-ar{margin:6px 0 0;font-size:17px;color:var(--ink-3);letter-spacing:0;line-height:1.5;font-weight:var(--fw-medium)}.ph__metric{text-align:end}.ph__metric-v{font-family:var(--font-mono, ui-monospace, monospace);font-variant-numeric:tabular-nums;font-size:22px;font-weight:var(--fw-medium);letter-spacing:-.02em;color:var(--ink)}.ph__metric-l{font-size:11px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.06em}.ph__hd-actions{display:flex;flex-direction:column;align-items:flex-end;gap:10px}.ph__hd-btns{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}.ph--listing,.ph--detail{margin-bottom:0}@container (width <= 640px){.ph__row{flex-direction:column;align-items:flex-start;gap:16px}.ph__r{align-items:flex-start;flex-flow:row wrap}.ph--detail .ph__r{flex-direction:row-reverse}.ph__title{font-size:26px}.ph__title--detail{font-size:24px}}@media(width<=640px){.ph__row{flex-direction:column;align-items:flex-start;gap:16px}.ph__r{align-items:flex-start;flex-flow:row wrap}.ph--detail .ph__r{flex-direction:row-reverse}.ph__title{font-size:26px}.ph__title--detail{font-size:24px}}\n"] }]
|
|
22836
23293
|
}], propDecorators: { variant: [{
|
|
22837
23294
|
type: Input
|
|
22838
23295
|
}], title: [{
|
|
@@ -23518,14 +23975,14 @@ class FlyAppTopbarComponent {
|
|
|
23518
23975
|
this.rows()[index]?.nativeElement.focus();
|
|
23519
23976
|
}
|
|
23520
23977
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlyAppTopbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
23521
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlyAppTopbarComponent, isStandalone: true, selector: "fly-app-topbar", inputs: { brandLabelKey: { classPropertyName: "brandLabelKey", publicName: "brandLabelKey", isSignal: true, isRequired: false, transformFunction: null }, sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null }, activeKey: { classPropertyName: "activeKey", publicName: "activeKey", isSignal: true, isRequired: false, transformFunction: null }, footerLabel: { classPropertyName: "footerLabel", publicName: "footerLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { moduleSelected: "moduleSelected", brandSelected: "brandSelected" }, host: { listeners: { "document:keydown.escape": "onEscape()" }, properties: { "class.fly-topbar--docked": "docked()" } }, queries: [{ propertyName: "icons", predicate: FlyModuleIconDirective, isSignal: true }], viewQueries: [{ propertyName: "rows", predicate: ["row"], descendants: true, isSignal: true }, { propertyName: "trigger", predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: "<header class=\"fly-topbar\">\r\n <div class=\"fly-topbar__lead\">\r\n <button\r\n type=\"button\"\r\n class=\"fly-topbar__brand\"\r\n (click)=\"brandSelected.emit()\"\r\n [attr.aria-label]=\"brandAriaLabel()\"\r\n >\r\n <span class=\"fly-topbar__brand-mark\"><ng-content select=\"[app-topbar-brand]\" /></span>\r\n @if (brandLabelKey(); as key) {\r\n <span class=\"fly-topbar__brand-name\">{{ key | translate }}</span>\r\n }\r\n </button>\r\n\r\n @if (activeModule()) {\r\n <span class=\"fly-topbar__sep\" aria-hidden=\"true\">/</span>\r\n }\r\n\r\n <div class=\"fly-topbar__anchor\" (flyClickOutside)=\"close()\" [flyClickOutsideEnabled]=\"open()\">\r\n <button\r\n #trigger\r\n type=\"button\"\r\n class=\"fly-topbar__module\"\r\n [class.fly-topbar__module--on]=\"open()\"\r\n (click)=\"toggle()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n aria-haspopup=\"menu\"\r\n [attr.aria-expanded]=\"open()\"\r\n [attr.aria-label]=\"triggerAriaLabel()\"\r\n >\r\n @if (activeModule(); as active) {\r\n <span class=\"fly-topbar__module-icon\">\r\n @if (iconFor(active.key); as tpl) {\r\n <ng-container *ngTemplateOutlet=\"tpl\" />\r\n } @else if (active.iconClass) {\r\n <i [class]=\"active.iconClass\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n <span class=\"fly-topbar__module-label\">{{ active.labelKey | translate }}</span>\r\n } @else {\r\n <span class=\"fly-topbar__module-label fly-topbar__module-label--empty\">\r\n {{ 'ui.nav.selectModule' | translate }}\r\n </span>\r\n }\r\n <svg class=\"fly-topbar__chevron\" width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n\r\n @if (open()) {\r\n <div class=\"fly-topbar__pop\" role=\"menu\" [attr.aria-label]=\"switchAriaLabel()\">\r\n <div class=\"fly-topbar__cols\">\r\n @for (section of sections(); track $index) {\r\n <div class=\"fly-topbar__col\">\r\n @if (section.titleKey; as titleKey) {\r\n <div class=\"fly-topbar__col-title\">{{ titleKey | translate }}</div>\r\n }\r\n @for (mod of section.modules; track mod.key) {\r\n <button\r\n #row\r\n type=\"button\"\r\n class=\"fly-topbar__item\"\r\n [class.fly-topbar__item--on]=\"mod.key === activeKey()\"\r\n role=\"menuitem\"\r\n [disabled]=\"mod.disabled ?? false\"\r\n [attr.tabindex]=\"indexOf(mod) === focusIndex() ? 0 : -1\"\r\n [attr.aria-current]=\"mod.key === activeKey() ? 'page' : null\"\r\n (click)=\"select(mod)\"\r\n (keydown)=\"onRowKeydown($event)\"\r\n >\r\n <span class=\"fly-topbar__item-icon\">\r\n @if (iconFor(mod.key); as tpl) {\r\n <ng-container *ngTemplateOutlet=\"tpl\" />\r\n } @else if (mod.iconClass) {\r\n <i [class]=\"mod.iconClass\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n <span class=\"fly-topbar__item-main\">\r\n <span class=\"fly-topbar__item-label\">{{ mod.labelKey | translate }}</span>\r\n @if (mod.descKey; as descKey) {\r\n <span class=\"fly-topbar__item-desc\">{{ descKey | translate }}</span>\r\n }\r\n </span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (footerLabel()) {\r\n <div class=\"fly-topbar__foot\">{{ footerLabel() }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fly-topbar__actions\"><ng-content select=\"[app-topbar-actions]\" /></div>\r\n</header>\r\n", styles: [":host{display:block;position:relative;z-index:var(--z-sticky)}.fly-topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);border-block-end:1px solid var(--w1);background:var(--bg-3)}.fly-topbar__lead{display:flex;align-items:center;gap:var(--sp-2);min-inline-size:0}.fly-topbar__brand,.fly-topbar__module{display:inline-flex;align-items:center;gap:var(--sp-2);appearance:none;border:0;background:transparent;cursor:pointer;font:inherit;color:var(--ink);padding:var(--sp-1) var(--sp-2);border-radius:var(--r-md);transition:background var(--t-state)}.fly-topbar__brand:hover,.fly-topbar__module:hover{background:var(--bg-hover)}.fly-topbar__brand:focus-visible,.fly-topbar__module:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}.fly-topbar__brand-name{font-weight:var(--fw-semibold);white-space:nowrap}.fly-topbar__brand-mark{display:inline-flex;align-items:center;max-block-size:var(--icon-lg)}.fly-topbar__sep{color:var(--ink-4);-webkit-user-select:none;user-select:none}.fly-topbar__module{min-inline-size:0}.fly-topbar__module--on{background:var(--bg-hover)}.fly-topbar__module-label{font-weight:var(--fw-medium);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fly-topbar__module-label--empty{color:var(--ink-3)}.fly-topbar__module-icon,.fly-topbar__item-icon{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--icon-md);block-size:var(--icon-md);flex:0 0 auto;color:var(--accent)}.fly-topbar__chevron{flex:0 0 auto;color:var(--ink-3);transition:transform var(--t-state)}.fly-topbar__module--on .fly-topbar__chevron{transform:rotate(180deg)}.fly-topbar__anchor{position:relative}:host(.fly-topbar--docked){flex:1 1 auto;min-inline-size:0;z-index:auto}:host(.fly-topbar--docked) .fly-topbar{padding:0;background:transparent;border-block-end:0}:host(.fly-topbar--docked) .fly-topbar__lead{gap:2px}:host(.fly-topbar--docked) .fly-topbar__brand,:host(.fly-topbar--docked) .fly-topbar__module{block-size:26px;padding-block:0;padding-inline:var(--sp-2);border-radius:var(--r-sm);font-size:var(--text-sm);line-height:1;color:var(--label-secondary)}:host(.fly-topbar--docked) .fly-topbar__brand:hover,:host(.fly-topbar--docked) .fly-topbar__module:hover{background:var(--w08);color:var(--label-primary)}:host(.fly-topbar--docked) .fly-topbar__module{color:var(--label-primary)}:host(.fly-topbar--docked) .fly-topbar__module--on{background:var(--w08)}:host(.fly-topbar--docked) .fly-topbar__brand-name{font-weight:var(--fw-medium)}:host(.fly-topbar--docked) .fly-topbar__module-label{font-weight:var(--fw-semibold)}:host(.fly-topbar--docked) .fly-topbar__sep{color:var(--w28)}:host(.fly-topbar--docked) .fly-topbar__module-icon{inline-size:14px;block-size:14px}:host(.fly-topbar--docked) .fly-topbar__brand-mark{max-block-size:22px}:host(.fly-topbar--docked) .fly-topbar__brand-mark ::ng-deep *{max-inline-size:22px;max-block-size:22px}.fly-topbar__pop{position:absolute;inset-block-start:calc(100% + var(--sp-1));inset-inline-start:0;z-index:var(--z-overlay);min-inline-size:260px;max-inline-size:min(680px,92vw);padding:var(--sp-3);border-radius:var(--r-lg);border:1px solid var(--glass2-border);background-image:radial-gradient(70% 60% at 50% 100%,var(--w07),transparent 70%),linear-gradient(180deg,var(--w08),transparent 20%),linear-gradient(180deg,var(--mat-menu-solid-a),var(--mat-menu-solid-b));box-shadow:0 16px 40px #0003,0 4px 10px #0000001a,var(--glass2-inset)}@media(prefers-reduced-transparency:reduce){.fly-topbar__pop{border-color:transparent;background-color:light-dark(#eef2f8,#2c2c2e);background-image:none;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.fly-topbar__pop:before,.fly-topbar__pop:after{display:none}}@media(prefers-contrast:more){.fly-topbar__pop{border-color:var(--w6)}}@media(prefers-reduced-motion:reduce){.fly-topbar__pop:after{animation:none}}.fly-topbar__cols{display:flex;gap:var(--sp-4);align-items:flex-start}@media(width<=720px){.fly-topbar__cols{flex-direction:column;gap:var(--sp-3)}}.fly-topbar__col{display:flex;flex-direction:column;gap:2px;min-inline-size:220px;flex:1 1 0}.fly-topbar__col-title{font-size:var(--text-2xs);font-weight:var(--fw-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--ink-3);padding:var(--sp-2) var(--sp-2) var(--sp-1)}.fly-topbar__item{display:flex;align-items:flex-start;gap:var(--sp-2);inline-size:100%;text-align:start;appearance:none;border:0;background:transparent;cursor:pointer;font:inherit;color:var(--ink);padding:var(--sp-2);border-radius:var(--r-md);transition:background var(--t-state)}.fly-topbar__item:hover:not(:disabled){background:var(--w1)}.fly-topbar__item:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px}.fly-topbar__item:disabled{opacity:.5;cursor:default}.fly-topbar__item--on{background:var(--accent-soft);color:var(--on-accent-soft);box-shadow:inset 2px 0 0 0 var(--accent)}.fly-topbar__item-main{display:flex;flex-direction:column;gap:1px;min-inline-size:0}.fly-topbar__item-label{font-weight:var(--fw-medium);line-height:1.3}.fly-topbar__item-desc{font-size:var(--text-xs);color:var(--ink-3);line-height:1.35}.fly-topbar__foot{margin-block-start:var(--sp-3);padding-block-start:var(--sp-2);border-block-start:.5px solid var(--w08);font-size:var(--text-2xs);color:var(--ink-4)}.fly-topbar__actions{display:flex;align-items:center;gap:var(--sp-2);flex:0 0 auto}@media(forced-colors:active){.fly-topbar__item--on{border-inline-start:2px solid CanvasText}}@media(prefers-reduced-motion:reduce){.fly-topbar__brand,.fly-topbar__module,.fly-topbar__item,.fly-topbar__chevron{transition:none}}\n"], dependencies: [{ kind: "directive", type: FlyClickOutsideDirective, selector: "[flyClickOutside]", inputs: ["flyClickOutsideEnabled", "flyClickOutsideIgnore"], outputs: ["flyClickOutside"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
23978
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlyAppTopbarComponent, isStandalone: true, selector: "fly-app-topbar", inputs: { brandLabelKey: { classPropertyName: "brandLabelKey", publicName: "brandLabelKey", isSignal: true, isRequired: false, transformFunction: null }, sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null }, activeKey: { classPropertyName: "activeKey", publicName: "activeKey", isSignal: true, isRequired: false, transformFunction: null }, footerLabel: { classPropertyName: "footerLabel", publicName: "footerLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { moduleSelected: "moduleSelected", brandSelected: "brandSelected" }, host: { listeners: { "document:keydown.escape": "onEscape()" }, properties: { "class.fly-topbar--docked": "docked()" } }, queries: [{ propertyName: "icons", predicate: FlyModuleIconDirective, isSignal: true }], viewQueries: [{ propertyName: "rows", predicate: ["row"], descendants: true, isSignal: true }, { propertyName: "trigger", predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: "<header class=\"fly-topbar\">\r\n <div class=\"fly-topbar__lead\">\r\n <button\r\n type=\"button\"\r\n class=\"fly-topbar__brand\"\r\n (click)=\"brandSelected.emit()\"\r\n [attr.aria-label]=\"brandAriaLabel()\"\r\n >\r\n <span class=\"fly-topbar__brand-mark\"><ng-content select=\"[app-topbar-brand]\" /></span>\r\n @if (brandLabelKey(); as key) {\r\n <span class=\"fly-topbar__brand-name\">{{ key | translate }}</span>\r\n }\r\n </button>\r\n\r\n @if (activeModule()) {\r\n <span class=\"fly-topbar__sep\" aria-hidden=\"true\">/</span>\r\n }\r\n\r\n <div class=\"fly-topbar__anchor\" (flyClickOutside)=\"close()\" [flyClickOutsideEnabled]=\"open()\">\r\n <button\r\n #trigger\r\n type=\"button\"\r\n class=\"fly-topbar__module\"\r\n [class.fly-topbar__module--on]=\"open()\"\r\n (click)=\"toggle()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n aria-haspopup=\"menu\"\r\n [attr.aria-expanded]=\"open()\"\r\n [attr.aria-label]=\"triggerAriaLabel()\"\r\n >\r\n @if (activeModule(); as active) {\r\n <span class=\"fly-topbar__module-icon\">\r\n @if (iconFor(active.key); as tpl) {\r\n <ng-container *ngTemplateOutlet=\"tpl\" />\r\n } @else if (active.iconClass) {\r\n <i [class]=\"active.iconClass\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n <span class=\"fly-topbar__module-label\">{{ active.labelKey | translate }}</span>\r\n } @else {\r\n <span class=\"fly-topbar__module-label fly-topbar__module-label--empty\">\r\n {{ 'ui.nav.selectModule' | translate }}\r\n </span>\r\n }\r\n <svg class=\"fly-topbar__chevron\" width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n\r\n @if (open()) {\r\n <div class=\"fly-topbar__pop\" role=\"menu\" [attr.aria-label]=\"switchAriaLabel()\">\r\n <div class=\"fly-topbar__cols\">\r\n @for (section of sections(); track $index) {\r\n <div class=\"fly-topbar__col\">\r\n @if (section.titleKey; as titleKey) {\r\n <div class=\"fly-topbar__col-title\">{{ titleKey | translate }}</div>\r\n }\r\n @for (mod of section.modules; track mod.key) {\r\n <button\r\n #row\r\n type=\"button\"\r\n class=\"fly-topbar__item\"\r\n [class.fly-topbar__item--on]=\"mod.key === activeKey()\"\r\n role=\"menuitem\"\r\n [disabled]=\"mod.disabled ?? false\"\r\n [attr.tabindex]=\"indexOf(mod) === focusIndex() ? 0 : -1\"\r\n [attr.aria-current]=\"mod.key === activeKey() ? 'page' : null\"\r\n (click)=\"select(mod)\"\r\n (keydown)=\"onRowKeydown($event)\"\r\n >\r\n <span class=\"fly-topbar__item-icon\">\r\n @if (iconFor(mod.key); as tpl) {\r\n <ng-container *ngTemplateOutlet=\"tpl\" />\r\n } @else if (mod.iconClass) {\r\n <i [class]=\"mod.iconClass\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n <span class=\"fly-topbar__item-main\">\r\n <span class=\"fly-topbar__item-label\">{{ mod.labelKey | translate }}</span>\r\n @if (mod.descKey; as descKey) {\r\n <span class=\"fly-topbar__item-desc\">{{ descKey | translate }}</span>\r\n }\r\n </span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (footerLabel()) {\r\n <div class=\"fly-topbar__foot\">{{ footerLabel() }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fly-topbar__actions\"><ng-content select=\"[app-topbar-actions]\" /></div>\r\n</header>\r\n", styles: [":host{display:block;position:relative;z-index:var(--z-sticky)}.fly-topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);border-block-end:1px solid var(--w1);background:var(--bg-3)}.fly-topbar__lead{display:flex;align-items:center;gap:var(--sp-2);min-inline-size:0}.fly-topbar__brand,.fly-topbar__module{display:inline-flex;align-items:center;gap:var(--sp-2);appearance:none;border:0;background:transparent;cursor:pointer;font:inherit;color:var(--ink);padding:var(--sp-1) var(--sp-2);border-radius:var(--r-md);transition:background var(--t-state)}.fly-topbar__brand:hover,.fly-topbar__module:hover{background:var(--bg-hover)}.fly-topbar__brand:focus-visible,.fly-topbar__module:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}.fly-topbar__brand-name{font-weight:var(--fw-semibold);white-space:nowrap}.fly-topbar__brand-mark{display:inline-flex;align-items:center;max-block-size:var(--icon-lg)}.fly-topbar__sep{color:var(--ink-4);-webkit-user-select:none;user-select:none}.fly-topbar__module{min-inline-size:0}.fly-topbar__module--on{background:var(--bg-hover)}.fly-topbar__module-label{font-weight:var(--fw-medium);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fly-topbar__module-label--empty{color:var(--ink-3)}.fly-topbar__module-icon,.fly-topbar__item-icon{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--icon-md);block-size:var(--icon-md);flex:0 0 auto;color:var(--accent)}.fly-topbar__chevron{flex:0 0 auto;color:var(--ink-3);transition:transform var(--t-state)}.fly-topbar__module--on .fly-topbar__chevron{transform:rotate(180deg)}.fly-topbar__anchor{position:relative}:host(.fly-topbar--docked){flex:1 1 auto;min-inline-size:0;z-index:auto}:host(.fly-topbar--docked) .fly-topbar{padding:0;background:transparent;border-block-end:0}:host(.fly-topbar--docked) .fly-topbar__lead{gap:2px}:host(.fly-topbar--docked) .fly-topbar__brand,:host(.fly-topbar--docked) .fly-topbar__module{block-size:26px;padding-block:0;padding-inline:var(--sp-2);border-radius:var(--r-md);gap:6px;font-size:var(--text-sm);line-height:1;color:var(--w55)}:host(.fly-topbar--docked) .fly-topbar__brand:hover,:host(.fly-topbar--docked) .fly-topbar__module:hover{background:var(--w08);color:var(--label-primary)}:host(.fly-topbar--docked) .fly-topbar__module{background:var(--w06);color:var(--label-primary)}:host(.fly-topbar--docked) .fly-topbar__module--on{background:var(--w08)}:host(.fly-topbar--docked) .fly-topbar__brand-name{font-weight:var(--fw-medium)}:host(.fly-topbar--docked) .fly-topbar__module-label{font-weight:var(--fw-semibold);letter-spacing:-.006em}:host-context([dir=rtl]) :host(.fly-topbar--docked) .fly-topbar__module-label{letter-spacing:normal}:host(.fly-topbar--docked) .fly-topbar__sep{position:relative;display:inline-block;inline-size:7px;block-size:7px;color:transparent;border-block-start:1.5px solid var(--w28);border-inline-end:1.5px solid var(--w28);transform:rotate(45deg)}:host-context([dir=rtl]) :host(.fly-topbar--docked) .fly-topbar__sep{transform:rotate(-135deg)}:host(.fly-topbar--docked) .fly-topbar__actions{display:none}:host(.fly-topbar--docked) .fly-topbar__module-icon{inline-size:14px;block-size:14px}:host(.fly-topbar--docked) .fly-topbar__brand-mark{max-block-size:14px}:host(.fly-topbar--docked) .fly-topbar__brand-mark ::ng-deep *{max-inline-size:14px;max-block-size:14px}.fly-topbar__pop{position:absolute;inset-block-start:calc(100% + var(--sp-1));inset-inline-start:0;z-index:var(--z-overlay);min-inline-size:260px;max-inline-size:min(680px,92vw);padding:var(--sp-3);border-radius:var(--r-lg);border:1px solid var(--glass2-border);background-image:radial-gradient(70% 60% at 50% 100%,var(--w07),transparent 70%),linear-gradient(180deg,var(--w08),transparent 20%),linear-gradient(180deg,var(--mat-menu-solid-a),var(--mat-menu-solid-b));box-shadow:0 16px 40px #0003,0 4px 10px #0000001a,var(--glass2-inset)}@media(prefers-reduced-transparency:reduce){.fly-topbar__pop{border-color:transparent;background-color:light-dark(#eef2f8,#2c2c2e);background-image:none;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.fly-topbar__pop:before,.fly-topbar__pop:after{display:none}}@media(prefers-contrast:more){.fly-topbar__pop{border-color:var(--w6)}}@media(prefers-reduced-motion:reduce){.fly-topbar__pop:after{animation:none}}.fly-topbar__cols{display:flex;gap:var(--sp-4);align-items:flex-start}@media(width<=720px){.fly-topbar__cols{flex-direction:column;gap:var(--sp-3)}}.fly-topbar__col{display:flex;flex-direction:column;gap:2px;min-inline-size:220px;flex:1 1 0}.fly-topbar__col-title{font-size:var(--text-2xs);font-weight:var(--fw-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--ink-3);padding:var(--sp-2) var(--sp-2) var(--sp-1)}.fly-topbar__item{display:flex;align-items:flex-start;gap:var(--sp-2);inline-size:100%;text-align:start;appearance:none;border:0;background:transparent;cursor:pointer;font:inherit;color:var(--ink);padding:var(--sp-2);border-radius:var(--r-md);transition:background var(--t-state)}.fly-topbar__item:hover:not(:disabled){background:var(--w1)}.fly-topbar__item:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px}.fly-topbar__item:disabled{opacity:.5;cursor:default}.fly-topbar__item--on{background:var(--accent-soft);color:var(--on-accent-soft);box-shadow:inset 2px 0 0 0 var(--accent)}.fly-topbar__item-main{display:flex;flex-direction:column;gap:1px;min-inline-size:0}.fly-topbar__item-label{font-weight:var(--fw-medium);line-height:1.3}.fly-topbar__item-desc{font-size:var(--text-xs);color:var(--ink-3);line-height:1.35}.fly-topbar__foot{margin-block-start:var(--sp-3);padding-block-start:var(--sp-2);border-block-start:.5px solid var(--w08);font-size:var(--text-2xs);color:var(--ink-4)}.fly-topbar__actions{display:flex;align-items:center;gap:var(--sp-2);flex:0 0 auto}@media(forced-colors:active){.fly-topbar__item--on{border-inline-start:2px solid CanvasText}}@media(prefers-reduced-motion:reduce){.fly-topbar__brand,.fly-topbar__module,.fly-topbar__item,.fly-topbar__chevron{transition:none}}\n"], dependencies: [{ kind: "directive", type: FlyClickOutsideDirective, selector: "[flyClickOutside]", inputs: ["flyClickOutsideEnabled", "flyClickOutsideIgnore"], outputs: ["flyClickOutside"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
23522
23979
|
}
|
|
23523
23980
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlyAppTopbarComponent, decorators: [{
|
|
23524
23981
|
type: Component,
|
|
23525
23982
|
args: [{ selector: 'fly-app-topbar', standalone: true, imports: [TranslatePipe, FlyClickOutsideDirective, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
23526
23983
|
'(document:keydown.escape)': 'onEscape()',
|
|
23527
23984
|
'[class.fly-topbar--docked]': 'docked()',
|
|
23528
|
-
}, template: "<header class=\"fly-topbar\">\r\n <div class=\"fly-topbar__lead\">\r\n <button\r\n type=\"button\"\r\n class=\"fly-topbar__brand\"\r\n (click)=\"brandSelected.emit()\"\r\n [attr.aria-label]=\"brandAriaLabel()\"\r\n >\r\n <span class=\"fly-topbar__brand-mark\"><ng-content select=\"[app-topbar-brand]\" /></span>\r\n @if (brandLabelKey(); as key) {\r\n <span class=\"fly-topbar__brand-name\">{{ key | translate }}</span>\r\n }\r\n </button>\r\n\r\n @if (activeModule()) {\r\n <span class=\"fly-topbar__sep\" aria-hidden=\"true\">/</span>\r\n }\r\n\r\n <div class=\"fly-topbar__anchor\" (flyClickOutside)=\"close()\" [flyClickOutsideEnabled]=\"open()\">\r\n <button\r\n #trigger\r\n type=\"button\"\r\n class=\"fly-topbar__module\"\r\n [class.fly-topbar__module--on]=\"open()\"\r\n (click)=\"toggle()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n aria-haspopup=\"menu\"\r\n [attr.aria-expanded]=\"open()\"\r\n [attr.aria-label]=\"triggerAriaLabel()\"\r\n >\r\n @if (activeModule(); as active) {\r\n <span class=\"fly-topbar__module-icon\">\r\n @if (iconFor(active.key); as tpl) {\r\n <ng-container *ngTemplateOutlet=\"tpl\" />\r\n } @else if (active.iconClass) {\r\n <i [class]=\"active.iconClass\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n <span class=\"fly-topbar__module-label\">{{ active.labelKey | translate }}</span>\r\n } @else {\r\n <span class=\"fly-topbar__module-label fly-topbar__module-label--empty\">\r\n {{ 'ui.nav.selectModule' | translate }}\r\n </span>\r\n }\r\n <svg class=\"fly-topbar__chevron\" width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n\r\n @if (open()) {\r\n <div class=\"fly-topbar__pop\" role=\"menu\" [attr.aria-label]=\"switchAriaLabel()\">\r\n <div class=\"fly-topbar__cols\">\r\n @for (section of sections(); track $index) {\r\n <div class=\"fly-topbar__col\">\r\n @if (section.titleKey; as titleKey) {\r\n <div class=\"fly-topbar__col-title\">{{ titleKey | translate }}</div>\r\n }\r\n @for (mod of section.modules; track mod.key) {\r\n <button\r\n #row\r\n type=\"button\"\r\n class=\"fly-topbar__item\"\r\n [class.fly-topbar__item--on]=\"mod.key === activeKey()\"\r\n role=\"menuitem\"\r\n [disabled]=\"mod.disabled ?? false\"\r\n [attr.tabindex]=\"indexOf(mod) === focusIndex() ? 0 : -1\"\r\n [attr.aria-current]=\"mod.key === activeKey() ? 'page' : null\"\r\n (click)=\"select(mod)\"\r\n (keydown)=\"onRowKeydown($event)\"\r\n >\r\n <span class=\"fly-topbar__item-icon\">\r\n @if (iconFor(mod.key); as tpl) {\r\n <ng-container *ngTemplateOutlet=\"tpl\" />\r\n } @else if (mod.iconClass) {\r\n <i [class]=\"mod.iconClass\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n <span class=\"fly-topbar__item-main\">\r\n <span class=\"fly-topbar__item-label\">{{ mod.labelKey | translate }}</span>\r\n @if (mod.descKey; as descKey) {\r\n <span class=\"fly-topbar__item-desc\">{{ descKey | translate }}</span>\r\n }\r\n </span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (footerLabel()) {\r\n <div class=\"fly-topbar__foot\">{{ footerLabel() }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fly-topbar__actions\"><ng-content select=\"[app-topbar-actions]\" /></div>\r\n</header>\r\n", styles: [":host{display:block;position:relative;z-index:var(--z-sticky)}.fly-topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);border-block-end:1px solid var(--w1);background:var(--bg-3)}.fly-topbar__lead{display:flex;align-items:center;gap:var(--sp-2);min-inline-size:0}.fly-topbar__brand,.fly-topbar__module{display:inline-flex;align-items:center;gap:var(--sp-2);appearance:none;border:0;background:transparent;cursor:pointer;font:inherit;color:var(--ink);padding:var(--sp-1) var(--sp-2);border-radius:var(--r-md);transition:background var(--t-state)}.fly-topbar__brand:hover,.fly-topbar__module:hover{background:var(--bg-hover)}.fly-topbar__brand:focus-visible,.fly-topbar__module:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}.fly-topbar__brand-name{font-weight:var(--fw-semibold);white-space:nowrap}.fly-topbar__brand-mark{display:inline-flex;align-items:center;max-block-size:var(--icon-lg)}.fly-topbar__sep{color:var(--ink-4);-webkit-user-select:none;user-select:none}.fly-topbar__module{min-inline-size:0}.fly-topbar__module--on{background:var(--bg-hover)}.fly-topbar__module-label{font-weight:var(--fw-medium);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fly-topbar__module-label--empty{color:var(--ink-3)}.fly-topbar__module-icon,.fly-topbar__item-icon{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--icon-md);block-size:var(--icon-md);flex:0 0 auto;color:var(--accent)}.fly-topbar__chevron{flex:0 0 auto;color:var(--ink-3);transition:transform var(--t-state)}.fly-topbar__module--on .fly-topbar__chevron{transform:rotate(180deg)}.fly-topbar__anchor{position:relative}:host(.fly-topbar--docked){flex:1 1 auto;min-inline-size:0;z-index:auto}:host(.fly-topbar--docked) .fly-topbar{padding:0;background:transparent;border-block-end:0}:host(.fly-topbar--docked) .fly-topbar__lead{gap:2px}:host(.fly-topbar--docked) .fly-topbar__brand,:host(.fly-topbar--docked) .fly-topbar__module{block-size:26px;padding-block:0;padding-inline:var(--sp-2);border-radius:var(--r-sm);font-size:var(--text-sm);line-height:1;color:var(--label-secondary)}:host(.fly-topbar--docked) .fly-topbar__brand:hover,:host(.fly-topbar--docked) .fly-topbar__module:hover{background:var(--w08);color:var(--label-primary)}:host(.fly-topbar--docked) .fly-topbar__module{color:var(--label-primary)}:host(.fly-topbar--docked) .fly-topbar__module--on{background:var(--w08)}:host(.fly-topbar--docked) .fly-topbar__brand-name{font-weight:var(--fw-medium)}:host(.fly-topbar--docked) .fly-topbar__module-label{font-weight:var(--fw-semibold)}:host(.fly-topbar--docked) .fly-topbar__sep{color:var(--w28)}:host(.fly-topbar--docked) .fly-topbar__module-icon{inline-size:14px;block-size:14px}:host(.fly-topbar--docked) .fly-topbar__brand-mark{max-block-size:22px}:host(.fly-topbar--docked) .fly-topbar__brand-mark ::ng-deep *{max-inline-size:22px;max-block-size:22px}.fly-topbar__pop{position:absolute;inset-block-start:calc(100% + var(--sp-1));inset-inline-start:0;z-index:var(--z-overlay);min-inline-size:260px;max-inline-size:min(680px,92vw);padding:var(--sp-3);border-radius:var(--r-lg);border:1px solid var(--glass2-border);background-image:radial-gradient(70% 60% at 50% 100%,var(--w07),transparent 70%),linear-gradient(180deg,var(--w08),transparent 20%),linear-gradient(180deg,var(--mat-menu-solid-a),var(--mat-menu-solid-b));box-shadow:0 16px 40px #0003,0 4px 10px #0000001a,var(--glass2-inset)}@media(prefers-reduced-transparency:reduce){.fly-topbar__pop{border-color:transparent;background-color:light-dark(#eef2f8,#2c2c2e);background-image:none;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.fly-topbar__pop:before,.fly-topbar__pop:after{display:none}}@media(prefers-contrast:more){.fly-topbar__pop{border-color:var(--w6)}}@media(prefers-reduced-motion:reduce){.fly-topbar__pop:after{animation:none}}.fly-topbar__cols{display:flex;gap:var(--sp-4);align-items:flex-start}@media(width<=720px){.fly-topbar__cols{flex-direction:column;gap:var(--sp-3)}}.fly-topbar__col{display:flex;flex-direction:column;gap:2px;min-inline-size:220px;flex:1 1 0}.fly-topbar__col-title{font-size:var(--text-2xs);font-weight:var(--fw-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--ink-3);padding:var(--sp-2) var(--sp-2) var(--sp-1)}.fly-topbar__item{display:flex;align-items:flex-start;gap:var(--sp-2);inline-size:100%;text-align:start;appearance:none;border:0;background:transparent;cursor:pointer;font:inherit;color:var(--ink);padding:var(--sp-2);border-radius:var(--r-md);transition:background var(--t-state)}.fly-topbar__item:hover:not(:disabled){background:var(--w1)}.fly-topbar__item:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px}.fly-topbar__item:disabled{opacity:.5;cursor:default}.fly-topbar__item--on{background:var(--accent-soft);color:var(--on-accent-soft);box-shadow:inset 2px 0 0 0 var(--accent)}.fly-topbar__item-main{display:flex;flex-direction:column;gap:1px;min-inline-size:0}.fly-topbar__item-label{font-weight:var(--fw-medium);line-height:1.3}.fly-topbar__item-desc{font-size:var(--text-xs);color:var(--ink-3);line-height:1.35}.fly-topbar__foot{margin-block-start:var(--sp-3);padding-block-start:var(--sp-2);border-block-start:.5px solid var(--w08);font-size:var(--text-2xs);color:var(--ink-4)}.fly-topbar__actions{display:flex;align-items:center;gap:var(--sp-2);flex:0 0 auto}@media(forced-colors:active){.fly-topbar__item--on{border-inline-start:2px solid CanvasText}}@media(prefers-reduced-motion:reduce){.fly-topbar__brand,.fly-topbar__module,.fly-topbar__item,.fly-topbar__chevron{transition:none}}\n"] }]
|
|
23985
|
+
}, template: "<header class=\"fly-topbar\">\r\n <div class=\"fly-topbar__lead\">\r\n <button\r\n type=\"button\"\r\n class=\"fly-topbar__brand\"\r\n (click)=\"brandSelected.emit()\"\r\n [attr.aria-label]=\"brandAriaLabel()\"\r\n >\r\n <span class=\"fly-topbar__brand-mark\"><ng-content select=\"[app-topbar-brand]\" /></span>\r\n @if (brandLabelKey(); as key) {\r\n <span class=\"fly-topbar__brand-name\">{{ key | translate }}</span>\r\n }\r\n </button>\r\n\r\n @if (activeModule()) {\r\n <span class=\"fly-topbar__sep\" aria-hidden=\"true\">/</span>\r\n }\r\n\r\n <div class=\"fly-topbar__anchor\" (flyClickOutside)=\"close()\" [flyClickOutsideEnabled]=\"open()\">\r\n <button\r\n #trigger\r\n type=\"button\"\r\n class=\"fly-topbar__module\"\r\n [class.fly-topbar__module--on]=\"open()\"\r\n (click)=\"toggle()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n aria-haspopup=\"menu\"\r\n [attr.aria-expanded]=\"open()\"\r\n [attr.aria-label]=\"triggerAriaLabel()\"\r\n >\r\n @if (activeModule(); as active) {\r\n <span class=\"fly-topbar__module-icon\">\r\n @if (iconFor(active.key); as tpl) {\r\n <ng-container *ngTemplateOutlet=\"tpl\" />\r\n } @else if (active.iconClass) {\r\n <i [class]=\"active.iconClass\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n <span class=\"fly-topbar__module-label\">{{ active.labelKey | translate }}</span>\r\n } @else {\r\n <span class=\"fly-topbar__module-label fly-topbar__module-label--empty\">\r\n {{ 'ui.nav.selectModule' | translate }}\r\n </span>\r\n }\r\n <svg class=\"fly-topbar__chevron\" width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n aria-hidden=\"true\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n\r\n @if (open()) {\r\n <div class=\"fly-topbar__pop\" role=\"menu\" [attr.aria-label]=\"switchAriaLabel()\">\r\n <div class=\"fly-topbar__cols\">\r\n @for (section of sections(); track $index) {\r\n <div class=\"fly-topbar__col\">\r\n @if (section.titleKey; as titleKey) {\r\n <div class=\"fly-topbar__col-title\">{{ titleKey | translate }}</div>\r\n }\r\n @for (mod of section.modules; track mod.key) {\r\n <button\r\n #row\r\n type=\"button\"\r\n class=\"fly-topbar__item\"\r\n [class.fly-topbar__item--on]=\"mod.key === activeKey()\"\r\n role=\"menuitem\"\r\n [disabled]=\"mod.disabled ?? false\"\r\n [attr.tabindex]=\"indexOf(mod) === focusIndex() ? 0 : -1\"\r\n [attr.aria-current]=\"mod.key === activeKey() ? 'page' : null\"\r\n (click)=\"select(mod)\"\r\n (keydown)=\"onRowKeydown($event)\"\r\n >\r\n <span class=\"fly-topbar__item-icon\">\r\n @if (iconFor(mod.key); as tpl) {\r\n <ng-container *ngTemplateOutlet=\"tpl\" />\r\n } @else if (mod.iconClass) {\r\n <i [class]=\"mod.iconClass\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n <span class=\"fly-topbar__item-main\">\r\n <span class=\"fly-topbar__item-label\">{{ mod.labelKey | translate }}</span>\r\n @if (mod.descKey; as descKey) {\r\n <span class=\"fly-topbar__item-desc\">{{ descKey | translate }}</span>\r\n }\r\n </span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n @if (footerLabel()) {\r\n <div class=\"fly-topbar__foot\">{{ footerLabel() }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fly-topbar__actions\"><ng-content select=\"[app-topbar-actions]\" /></div>\r\n</header>\r\n", styles: [":host{display:block;position:relative;z-index:var(--z-sticky)}.fly-topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-2) var(--sp-3);border-block-end:1px solid var(--w1);background:var(--bg-3)}.fly-topbar__lead{display:flex;align-items:center;gap:var(--sp-2);min-inline-size:0}.fly-topbar__brand,.fly-topbar__module{display:inline-flex;align-items:center;gap:var(--sp-2);appearance:none;border:0;background:transparent;cursor:pointer;font:inherit;color:var(--ink);padding:var(--sp-1) var(--sp-2);border-radius:var(--r-md);transition:background var(--t-state)}.fly-topbar__brand:hover,.fly-topbar__module:hover{background:var(--bg-hover)}.fly-topbar__brand:focus-visible,.fly-topbar__module:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}.fly-topbar__brand-name{font-weight:var(--fw-semibold);white-space:nowrap}.fly-topbar__brand-mark{display:inline-flex;align-items:center;max-block-size:var(--icon-lg)}.fly-topbar__sep{color:var(--ink-4);-webkit-user-select:none;user-select:none}.fly-topbar__module{min-inline-size:0}.fly-topbar__module--on{background:var(--bg-hover)}.fly-topbar__module-label{font-weight:var(--fw-medium);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fly-topbar__module-label--empty{color:var(--ink-3)}.fly-topbar__module-icon,.fly-topbar__item-icon{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--icon-md);block-size:var(--icon-md);flex:0 0 auto;color:var(--accent)}.fly-topbar__chevron{flex:0 0 auto;color:var(--ink-3);transition:transform var(--t-state)}.fly-topbar__module--on .fly-topbar__chevron{transform:rotate(180deg)}.fly-topbar__anchor{position:relative}:host(.fly-topbar--docked){flex:1 1 auto;min-inline-size:0;z-index:auto}:host(.fly-topbar--docked) .fly-topbar{padding:0;background:transparent;border-block-end:0}:host(.fly-topbar--docked) .fly-topbar__lead{gap:2px}:host(.fly-topbar--docked) .fly-topbar__brand,:host(.fly-topbar--docked) .fly-topbar__module{block-size:26px;padding-block:0;padding-inline:var(--sp-2);border-radius:var(--r-md);gap:6px;font-size:var(--text-sm);line-height:1;color:var(--w55)}:host(.fly-topbar--docked) .fly-topbar__brand:hover,:host(.fly-topbar--docked) .fly-topbar__module:hover{background:var(--w08);color:var(--label-primary)}:host(.fly-topbar--docked) .fly-topbar__module{background:var(--w06);color:var(--label-primary)}:host(.fly-topbar--docked) .fly-topbar__module--on{background:var(--w08)}:host(.fly-topbar--docked) .fly-topbar__brand-name{font-weight:var(--fw-medium)}:host(.fly-topbar--docked) .fly-topbar__module-label{font-weight:var(--fw-semibold);letter-spacing:-.006em}:host-context([dir=rtl]) :host(.fly-topbar--docked) .fly-topbar__module-label{letter-spacing:normal}:host(.fly-topbar--docked) .fly-topbar__sep{position:relative;display:inline-block;inline-size:7px;block-size:7px;color:transparent;border-block-start:1.5px solid var(--w28);border-inline-end:1.5px solid var(--w28);transform:rotate(45deg)}:host-context([dir=rtl]) :host(.fly-topbar--docked) .fly-topbar__sep{transform:rotate(-135deg)}:host(.fly-topbar--docked) .fly-topbar__actions{display:none}:host(.fly-topbar--docked) .fly-topbar__module-icon{inline-size:14px;block-size:14px}:host(.fly-topbar--docked) .fly-topbar__brand-mark{max-block-size:14px}:host(.fly-topbar--docked) .fly-topbar__brand-mark ::ng-deep *{max-inline-size:14px;max-block-size:14px}.fly-topbar__pop{position:absolute;inset-block-start:calc(100% + var(--sp-1));inset-inline-start:0;z-index:var(--z-overlay);min-inline-size:260px;max-inline-size:min(680px,92vw);padding:var(--sp-3);border-radius:var(--r-lg);border:1px solid var(--glass2-border);background-image:radial-gradient(70% 60% at 50% 100%,var(--w07),transparent 70%),linear-gradient(180deg,var(--w08),transparent 20%),linear-gradient(180deg,var(--mat-menu-solid-a),var(--mat-menu-solid-b));box-shadow:0 16px 40px #0003,0 4px 10px #0000001a,var(--glass2-inset)}@media(prefers-reduced-transparency:reduce){.fly-topbar__pop{border-color:transparent;background-color:light-dark(#eef2f8,#2c2c2e);background-image:none;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.fly-topbar__pop:before,.fly-topbar__pop:after{display:none}}@media(prefers-contrast:more){.fly-topbar__pop{border-color:var(--w6)}}@media(prefers-reduced-motion:reduce){.fly-topbar__pop:after{animation:none}}.fly-topbar__cols{display:flex;gap:var(--sp-4);align-items:flex-start}@media(width<=720px){.fly-topbar__cols{flex-direction:column;gap:var(--sp-3)}}.fly-topbar__col{display:flex;flex-direction:column;gap:2px;min-inline-size:220px;flex:1 1 0}.fly-topbar__col-title{font-size:var(--text-2xs);font-weight:var(--fw-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--ink-3);padding:var(--sp-2) var(--sp-2) var(--sp-1)}.fly-topbar__item{display:flex;align-items:flex-start;gap:var(--sp-2);inline-size:100%;text-align:start;appearance:none;border:0;background:transparent;cursor:pointer;font:inherit;color:var(--ink);padding:var(--sp-2);border-radius:var(--r-md);transition:background var(--t-state)}.fly-topbar__item:hover:not(:disabled){background:var(--w1)}.fly-topbar__item:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px}.fly-topbar__item:disabled{opacity:.5;cursor:default}.fly-topbar__item--on{background:var(--accent-soft);color:var(--on-accent-soft);box-shadow:inset 2px 0 0 0 var(--accent)}.fly-topbar__item-main{display:flex;flex-direction:column;gap:1px;min-inline-size:0}.fly-topbar__item-label{font-weight:var(--fw-medium);line-height:1.3}.fly-topbar__item-desc{font-size:var(--text-xs);color:var(--ink-3);line-height:1.35}.fly-topbar__foot{margin-block-start:var(--sp-3);padding-block-start:var(--sp-2);border-block-start:.5px solid var(--w08);font-size:var(--text-2xs);color:var(--ink-4)}.fly-topbar__actions{display:flex;align-items:center;gap:var(--sp-2);flex:0 0 auto}@media(forced-colors:active){.fly-topbar__item--on{border-inline-start:2px solid CanvasText}}@media(prefers-reduced-motion:reduce){.fly-topbar__brand,.fly-topbar__module,.fly-topbar__item,.fly-topbar__chevron{transition:none}}\n"] }]
|
|
23529
23986
|
}], ctorParameters: () => [], propDecorators: { brandLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandLabelKey", required: false }] }], sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: false }] }], activeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeKey", required: false }] }], footerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerLabel", required: false }] }], moduleSelected: [{ type: i0.Output, args: ["moduleSelected"] }], brandSelected: [{ type: i0.Output, args: ["brandSelected"] }], icons: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FlyModuleIconDirective), { isSignal: true }] }], rows: [{ type: i0.ViewChildren, args: ['row', { isSignal: true }] }], trigger: [{ type: i0.ViewChildren, args: ['trigger', { isSignal: true }] }] } });
|
|
23530
23987
|
|
|
23531
23988
|
/**
|
|
@@ -24249,66 +24706,66 @@ class FlySegmentedComponent {
|
|
|
24249
24706
|
this.el.nativeElement.querySelectorAll('[role="radio"]')[next]?.focus();
|
|
24250
24707
|
}
|
|
24251
24708
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlySegmentedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
24252
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlySegmentedComponent, isStandalone: true, selector: "fly-segmented", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelKey: { classPropertyName: "ariaLabelKey", publicName: "ariaLabelKey", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", values: "valuesChange" }, ngImport: i0, template: `
|
|
24253
|
-
<div
|
|
24254
|
-
class="seg"
|
|
24255
|
-
[attr.data-variant]="variant()"
|
|
24256
|
-
[attr.role]="multiple() ? 'group' : 'radiogroup'"
|
|
24257
|
-
tabindex="-1"
|
|
24258
|
-
[attr.aria-label]="ariaLabelKey() ? (ariaLabelKey()! | translate) : null"
|
|
24259
|
-
(keydown)="onKey($event)"
|
|
24260
|
-
>
|
|
24261
|
-
@for (opt of options(); track opt.value) {
|
|
24262
|
-
<button
|
|
24263
|
-
type="button"
|
|
24264
|
-
class="seg__btn"
|
|
24265
|
-
[attr.role]="multiple() ? null : 'radio'"
|
|
24266
|
-
[class.on]="isOn(opt.value)"
|
|
24267
|
-
[attr.aria-checked]="multiple() ? null : isOn(opt.value)"
|
|
24268
|
-
[attr.aria-pressed]="multiple() ? isOn(opt.value) : null"
|
|
24269
|
-
[attr.tabindex]="multiple() ? null : tabIndexFor($index)"
|
|
24270
|
-
[disabled]="disabled()"
|
|
24271
|
-
(click)="select(opt.value)"
|
|
24272
|
-
>
|
|
24273
|
-
{{ opt.labelKey | translate }}
|
|
24274
|
-
@if (opt.count !== undefined) {
|
|
24275
|
-
<span class="seg__count">{{ opt.count }}</span>
|
|
24276
|
-
}
|
|
24277
|
-
</button>
|
|
24278
|
-
}
|
|
24279
|
-
</div>
|
|
24709
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FlySegmentedComponent, isStandalone: true, selector: "fly-segmented", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelKey: { classPropertyName: "ariaLabelKey", publicName: "ariaLabelKey", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", values: "valuesChange" }, ngImport: i0, template: `
|
|
24710
|
+
<div
|
|
24711
|
+
class="seg"
|
|
24712
|
+
[attr.data-variant]="variant()"
|
|
24713
|
+
[attr.role]="multiple() ? 'group' : 'radiogroup'"
|
|
24714
|
+
tabindex="-1"
|
|
24715
|
+
[attr.aria-label]="ariaLabelKey() ? (ariaLabelKey()! | translate) : null"
|
|
24716
|
+
(keydown)="onKey($event)"
|
|
24717
|
+
>
|
|
24718
|
+
@for (opt of options(); track opt.value) {
|
|
24719
|
+
<button
|
|
24720
|
+
type="button"
|
|
24721
|
+
class="seg__btn"
|
|
24722
|
+
[attr.role]="multiple() ? null : 'radio'"
|
|
24723
|
+
[class.on]="isOn(opt.value)"
|
|
24724
|
+
[attr.aria-checked]="multiple() ? null : isOn(opt.value)"
|
|
24725
|
+
[attr.aria-pressed]="multiple() ? isOn(opt.value) : null"
|
|
24726
|
+
[attr.tabindex]="multiple() ? null : tabIndexFor($index)"
|
|
24727
|
+
[disabled]="disabled()"
|
|
24728
|
+
(click)="select(opt.value)"
|
|
24729
|
+
>
|
|
24730
|
+
{{ opt.labelKey | translate }}
|
|
24731
|
+
@if (opt.count !== undefined) {
|
|
24732
|
+
<span class="seg__count">{{ opt.count }}</span>
|
|
24733
|
+
}
|
|
24734
|
+
</button>
|
|
24735
|
+
}
|
|
24736
|
+
</div>
|
|
24280
24737
|
`, isInline: true, styles: [":host{display:inline-flex}.seg{display:flex;flex-wrap:wrap;gap:3px;padding:3px;background:var(--w07);border:1px solid var(--w08);border-radius:99px}.seg[data-variant=pills]{gap:6px;padding:0;background:transparent;border:0;border-radius:0}.seg__btn{display:inline-flex;align-items:center;gap:6px;min-block-size:28px;padding:0 13px;border:0;background:transparent;color:var(--w55);font:500 12.5px/1.3 var(--font-family);font-family:inherit;border-radius:99px;cursor:pointer;white-space:nowrap;transition:background var(--nova-duration-hover) var(--nova-ease-micro),color var(--nova-duration-hover) var(--nova-ease-micro)}.seg__btn:hover{color:var(--w85)}.seg__btn:focus-visible{outline:3px solid var(--accent);outline-offset:3px}.seg__btn:disabled{opacity:.38;filter:saturate(.6);cursor:not-allowed}.seg__btn.on{background:var(--w14);color:var(--w95);font-weight:600;box-shadow:var(--shadow-card),var(--glass2-inset)}.seg__count{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:10.5px;padding:1px 5px;background:var(--w1);border-radius:4px;color:var(--w42)}.seg__btn.on .seg__count{color:var(--w55)}.seg[data-variant=pills] .seg__btn{min-block-size:30px;padding:0 12px;border:.5px solid var(--w12);background:var(--w04);color:var(--w7);border-radius:99px;transition:background var(--nova-duration-hover) var(--nova-ease-micro),color var(--nova-duration-hover) var(--nova-ease-micro),border-color var(--nova-duration-hover) var(--nova-ease-micro)}.seg[data-variant=pills] .seg__btn:hover:not(.on){background:var(--w08);border-color:var(--w2);color:var(--ink)}.seg[data-variant=pills] .seg__btn.on{border-color:transparent;background:var(--accent);color:var(--on-accent);font-weight:500;box-shadow:none}.seg[data-variant=pills] .seg__count{padding:2px 6px;border-radius:7px;font-family:var(--font-family);font-size:10.5px;font-weight:var(--fw-semibold);color:inherit}.seg[data-variant=pills] .seg__btn.on .seg__count{background:var(--w22);color:inherit}@media(pointer:coarse){.seg__btn,.seg[data-variant=pills] .seg__btn{min-block-size:44px}}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
24281
24738
|
}
|
|
24282
24739
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FlySegmentedComponent, decorators: [{
|
|
24283
24740
|
type: Component,
|
|
24284
|
-
args: [{ selector: 'fly-segmented', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
24285
|
-
<div
|
|
24286
|
-
class="seg"
|
|
24287
|
-
[attr.data-variant]="variant()"
|
|
24288
|
-
[attr.role]="multiple() ? 'group' : 'radiogroup'"
|
|
24289
|
-
tabindex="-1"
|
|
24290
|
-
[attr.aria-label]="ariaLabelKey() ? (ariaLabelKey()! | translate) : null"
|
|
24291
|
-
(keydown)="onKey($event)"
|
|
24292
|
-
>
|
|
24293
|
-
@for (opt of options(); track opt.value) {
|
|
24294
|
-
<button
|
|
24295
|
-
type="button"
|
|
24296
|
-
class="seg__btn"
|
|
24297
|
-
[attr.role]="multiple() ? null : 'radio'"
|
|
24298
|
-
[class.on]="isOn(opt.value)"
|
|
24299
|
-
[attr.aria-checked]="multiple() ? null : isOn(opt.value)"
|
|
24300
|
-
[attr.aria-pressed]="multiple() ? isOn(opt.value) : null"
|
|
24301
|
-
[attr.tabindex]="multiple() ? null : tabIndexFor($index)"
|
|
24302
|
-
[disabled]="disabled()"
|
|
24303
|
-
(click)="select(opt.value)"
|
|
24304
|
-
>
|
|
24305
|
-
{{ opt.labelKey | translate }}
|
|
24306
|
-
@if (opt.count !== undefined) {
|
|
24307
|
-
<span class="seg__count">{{ opt.count }}</span>
|
|
24308
|
-
}
|
|
24309
|
-
</button>
|
|
24310
|
-
}
|
|
24311
|
-
</div>
|
|
24741
|
+
args: [{ selector: 'fly-segmented', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
24742
|
+
<div
|
|
24743
|
+
class="seg"
|
|
24744
|
+
[attr.data-variant]="variant()"
|
|
24745
|
+
[attr.role]="multiple() ? 'group' : 'radiogroup'"
|
|
24746
|
+
tabindex="-1"
|
|
24747
|
+
[attr.aria-label]="ariaLabelKey() ? (ariaLabelKey()! | translate) : null"
|
|
24748
|
+
(keydown)="onKey($event)"
|
|
24749
|
+
>
|
|
24750
|
+
@for (opt of options(); track opt.value) {
|
|
24751
|
+
<button
|
|
24752
|
+
type="button"
|
|
24753
|
+
class="seg__btn"
|
|
24754
|
+
[attr.role]="multiple() ? null : 'radio'"
|
|
24755
|
+
[class.on]="isOn(opt.value)"
|
|
24756
|
+
[attr.aria-checked]="multiple() ? null : isOn(opt.value)"
|
|
24757
|
+
[attr.aria-pressed]="multiple() ? isOn(opt.value) : null"
|
|
24758
|
+
[attr.tabindex]="multiple() ? null : tabIndexFor($index)"
|
|
24759
|
+
[disabled]="disabled()"
|
|
24760
|
+
(click)="select(opt.value)"
|
|
24761
|
+
>
|
|
24762
|
+
{{ opt.labelKey | translate }}
|
|
24763
|
+
@if (opt.count !== undefined) {
|
|
24764
|
+
<span class="seg__count">{{ opt.count }}</span>
|
|
24765
|
+
}
|
|
24766
|
+
</button>
|
|
24767
|
+
}
|
|
24768
|
+
</div>
|
|
24312
24769
|
`, styles: [":host{display:inline-flex}.seg{display:flex;flex-wrap:wrap;gap:3px;padding:3px;background:var(--w07);border:1px solid var(--w08);border-radius:99px}.seg[data-variant=pills]{gap:6px;padding:0;background:transparent;border:0;border-radius:0}.seg__btn{display:inline-flex;align-items:center;gap:6px;min-block-size:28px;padding:0 13px;border:0;background:transparent;color:var(--w55);font:500 12.5px/1.3 var(--font-family);font-family:inherit;border-radius:99px;cursor:pointer;white-space:nowrap;transition:background var(--nova-duration-hover) var(--nova-ease-micro),color var(--nova-duration-hover) var(--nova-ease-micro)}.seg__btn:hover{color:var(--w85)}.seg__btn:focus-visible{outline:3px solid var(--accent);outline-offset:3px}.seg__btn:disabled{opacity:.38;filter:saturate(.6);cursor:not-allowed}.seg__btn.on{background:var(--w14);color:var(--w95);font-weight:600;box-shadow:var(--shadow-card),var(--glass2-inset)}.seg__count{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:10.5px;padding:1px 5px;background:var(--w1);border-radius:4px;color:var(--w42)}.seg__btn.on .seg__count{color:var(--w55)}.seg[data-variant=pills] .seg__btn{min-block-size:30px;padding:0 12px;border:.5px solid var(--w12);background:var(--w04);color:var(--w7);border-radius:99px;transition:background var(--nova-duration-hover) var(--nova-ease-micro),color var(--nova-duration-hover) var(--nova-ease-micro),border-color var(--nova-duration-hover) var(--nova-ease-micro)}.seg[data-variant=pills] .seg__btn:hover:not(.on){background:var(--w08);border-color:var(--w2);color:var(--ink)}.seg[data-variant=pills] .seg__btn.on{border-color:transparent;background:var(--accent);color:var(--on-accent);font-weight:500;box-shadow:none}.seg[data-variant=pills] .seg__count{padding:2px 6px;border-radius:7px;font-family:var(--font-family);font-size:10.5px;font-weight:var(--fw-semibold);color:inherit}.seg[data-variant=pills] .seg__btn.on .seg__count{background:var(--w22);color:inherit}@media(pointer:coarse){.seg__btn,.seg[data-variant=pills] .seg__btn{min-block-size:44px}}\n"] }]
|
|
24313
24770
|
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ariaLabelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelKey", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }, { type: i0.Output, args: ["valuesChange"] }] } });
|
|
24314
24771
|
|
|
@@ -26067,5 +26524,5 @@ const AUDIENCE_ERROR_CODES = {
|
|
|
26067
26524
|
* Generated bundle index. Do not edit.
|
|
26068
26525
|
*/
|
|
26069
26526
|
|
|
26070
|
-
export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, APP_LOOKUP, AUDIENCE_ERROR_CODES, AUDIENCE_LIMITS, AUDIENCE_PRESETS, AUDIENCE_TERM_KINDS, AgentActionBus, AgentActionUnsupportedDispatchError, AgentCommandRegistry, AgentDropRegistry, AgentFlightAnimator, AgentLookupRegistry, AgentPayloadOversizeError, AudienceBuilderComponent, AuthService, BilingualFieldComponent, CRON_MODES, CRON_WEEKDAYS, ContextMenuComponent, DEFAULT_AGENT_PAYLOAD_LIMITS, DEFAULT_FLY_THEME_MODE, DS_BASELINE_LOCALES, DashboardKpiComponent, DialogResult, ENTITY_LINK_LAUNCHER, EntityLookupComponent, FLYOS_LAUNCH_EVENT, FLYOS_LAUNCH_REQUEST_EVENT, FLYOS_REMOTE_ROUTE_EVENT, FLY_ACCENT_PROPERTY, FLY_ADMIN_ROLES, FLY_CHUNK_RELOAD_FLAG, FLY_COMPACT_THRESHOLD, FLY_COUNTRIES, FLY_CURRENCIES_BRIEF_ENDPOINT, FLY_EMOJI_BY_ID, FLY_EMOJI_CATEGORIES, FLY_EMOJI_CATEGORY_BY_ID, FLY_EMOJI_DEFAULT_CATEGORIES, FLY_EMOJI_PACK, FLY_EMOJI_PACK_MISSING_MESSAGE, FLY_EMOJI_PREVIEW_COUNT, FLY_EMPTY_VALUE, FLY_LOCALE_CATALOG, FLY_MAGIC_BAR_EVENT, FLY_MAGIC_BAR_ICONS, FLY_MAGIC_BAR_STORE_KEY, FLY_NF_CACHE_HEAL_FLAG, FLY_RELOAD_DEBOUNCE_MS, FLY_RELOAD_REARM_MS, FLY_REMOTE_BASE_PATH, FLY_REMOTE_CONTEXT_EVENT, FLY_REMOTE_CONTEXT_STORE_KEY, FLY_REMOTE_ROUTES, FLY_SCAN_PENDING_DEFAULT_DELAY_MS, FLY_SCAN_PENDING_MAX_RETRIES, FLY_SCAN_PENDING_STATUS, FLY_SEARCH_DEBOUNCE_MS, FLY_SKIN_TONES, FLY_STANDALONE_AUTH_CONFIG, FLY_THEME_MODE_IDS, FLY_VIEWPORT_IS_MOBILE, FLY_WINDOW_HELP_HINT_EVENT, FLY_WINDOW_PAGE_TITLE_EVENT, FlyAchievementProgressComponent, FlyActionMenuComponent, FlyActionStackComponent, FlyAgentDraggableDirective, FlyAnimatedEmojiComponent, FlyAppHomeComponent, FlyAppTopbarComponent, FlyAppUnavailableComponent, FlyAuthLiveRefresh, FlyBadgeWallComponent, FlyBlockUiComponent, FlyBreadcrumbComponent, FlyButtonComponent, FlyBytesPipe, FlyCaptchaComponent, FlyCardActionsComponent, FlyCardBodyComponent, FlyCardComponent, FlyCardFooterComponent, FlyCardGridComponent, FlyCardMediaComponent, FlyCardMetaComponent, FlyCardStatusComponent, FlyCardTitleComponent, FlyCellDirective, FlyCheckboxComponent, FlyChipComponent, FlyChunkReloadErrorHandler, FlyClickOutsideDirective, FlyCollapsibleComponent, FlyCommentThreadComponent, FlyCompactNumberPipe, FlyConfirmDialogComponent, FlyControlDirective, FlyCronBuilderComponent, FlyCurrencyCatalogService, FlyCurrencySelectorComponent, FlyDataTableComponent, FlyDatePipe, FlyDateTimePipe, FlyDebouncer, FlyDecimalNumberPipe, FlyDeepLinkPrefetchService, FlyDetailCardComponent, FlyDetailShellComponent, FlyDrawerComponent, FlyDurationPipe, FlyDynamicFormComponent, FlyEmojiPickerComponent, FlyFieldComponent, FlyFileDownloadService, FlyFileUploadComponent, FlyFilterPanelComponent, FlyFormBannerComponent, FlyFormFooterComponent, FlyFormGridComponent, FlyFormSectionComponent, FlyFullNumberPipe, FlyGanttComponent, FlyHubClient, FlyIconButtonComponent, FlyIfAdminDirective, FlyIfRoleDirective, FlyImageUploadComponent, FlyLeaderboardComponent, FlyMagicActionsComponent, FlyMetaListComponent, FlyModalComponent, FlyModerationQueueComponent, FlyModuleIconDirective, FlyMoneyPipe, FlyPaginationComponent, FlyPeoplePickerComponent, FlyPointsTierComponent, FlyProgressComponent, FlyRelativeTimePipe, FlyRemoteContextService, FlyRemoteRouter, FlyRemoteRouterOutletComponent, FlySearchInputComponent, FlySectionHeaderComponent, FlySecureSrcDirective, FlySegmentedComponent, FlySelectComponent, FlySkeletonComponent, FlySliderComponent, FlySparklineComponent, FlySpinnerComponent, FlyStandaloneAuthCallbackComponent, FlyStandaloneAuthService, FlyStandaloneMagicActionsComponent, FlyStateMessageComponent, FlyDynamicFormComponent as FlySurveyFormComponent, FlyTabComponent, FlyTabsComponent, FlyTagsInputComponent, FlyThemeService, FlyTimePipe, FlyToastService, FlyToggleComponent, FlyTooltipDirective, FlyTreeNavComponent, FlyTypeaheadComponent, FlyUserDirectoryService, FlyWindowHelpService, FlyWindowTitleService, FlyosPendingLaunchesGlobalKey, FlyosShellHandlesLaunchRequestsGlobalKey, FlyosShellOwnsHistoryGlobalKey, GANTT_DEPENDENCY_TYPES, GANTT_ROW_TINT_ALPHA, GANTT_ZOOMS, I18nService, LAUNCH_CONTEXT, MAGIC_BAR_SEARCH_WIDTH_DEFAULT, MAGIC_BAR_SEARCH_WIDTH_MAX, MAGIC_BAR_SEARCH_WIDTH_MIN, MagicBarRegistry, MessageBoxButtons, MessageBoxComponent, MessageBoxIcon, MessageBoxService, MockAuthService, NOVA_PRIORITY_TONE, NOVA_STATUS_TONE, NOVA_TONE_FALLBACK, OverlayStack, PRESENCE_COLORS, PageHeaderComponent, RTL_LOCALE_SET, SHARE_ORG_CHART_SYSTEM_KEY_APPS, SHARE_ORG_CHART_SYSTEM_KEY_DEFAULT, SHARE_PANEL_DEFAULT_FILE_LEVELS, STATE_DEFAULT_ICONS, STATE_DEFAULT_MESSAGE_KEYS, STEP_UP_CODE, STEP_UP_REAUTH_HANDLER, STEP_UP_RETURN_KEY, SUPPORTED_AGENT_PAYLOAD_VERSIONS, SharePanelComponent, SourceAppResolver, StandaloneWindowManagerService, StatusBadgeComponent, StepUpService, ToastHostComponent, TranslatePipe, WINDOW_DATA, WINDOW_HELP_HINT, WindowManagerService, applySkinTone, applySuggestion, ariaSort, buildCron, canConfirm, canGoNext, canGoPrev, captureFocus, clampMagicBarSearchWidth, clampPage, clampSliderValue, connectRemoteLaunch, enterActivatesNatively, filterGroup, filterSuggestions, findLocaleByDialect, findLocaleByPrefix, firstEnabledIndex, firstModuleIndex, flattenModules, flyAdminGuard, flyApiErrorMessage, flyCaptchaBase64Utf8, flyCaptchaBuildToken, flyCaptchaSha256Hex, flyCompactNumber, flyDebounced, flyDecimalNumber, flyDownloadBlob, flyDuration, flyEmojiFor, flyEmojiIsTonable, flyEmojiPackUrl, flyExportFileName, flyFirstEnabledIndex, flyFormatBytes, flyFormatDate, flyFormatDateTime, flyFormatMoney, flyFormatTime, flyFullNumber, flyNextEnabledIndex, flyNextSegmentIndex, flyRelativeTime, flyResolveActiveTab, flyRoleGuard, flyScanPendingDelayMs, flyScanRetry, flySignedCompact, flySkinToneLabelKey, flyStandaloneAuthGuard, flyStandaloneAuthInterceptor, flyToDateOnly, flyToPage, flyUnwrap, flyUnwrapCurrencies, flyUnwrapLenient, hasAnyRoleIn, healNativeFederationCacheOnce, initialExpanded, isAccentColor, isChunkLoadError, isFlyAuthStorePopulated, isNativeFederationCacheError, isRtlLocale, isRtlLocaleEntry, isValidCron, isValidSingleField, loadRemoteStyles, magicBarOwnerKey, magicBarSearchText, matchFlyRoutePattern, nextEnabledIndex, nextModuleIndex, nextSegmentIndex, nextSort, normalizeFlyTheme, normalizeRoles, novaPriorityToneVar, novaStatusToneVar, overlayStack, parseCron, parseStepUpChallenge, parseTags, prefetchRemoteStyles, presenceColorFor, printConsoleSecurityWarning, provideFlyChunkReloadRecovery, provideFlyEmojiPack, provideFlyStandaloneAuth, reloadOnceForChunkError, requestAppLaunch, resolveActiveModule, resolveActiveSection, resolveActiveTab, resolveCellValue, resolveStateIcon, resolveStateMessageKey, restoreFocus, sameTags, sectionHintKey, sectionLayout, sliderTrackGradient, sparklinePath, sparklinePoints, stepUpInterceptor, stepUpReturnKey, trimAgentPayload, trimAgentString, unloadRemoteStyles, utf8ByteLength, validateAgentPayload, warnIfEmbeddedSessionMissing };
|
|
26527
|
+
export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, APP_LOOKUP, AUDIENCE_ERROR_CODES, AUDIENCE_LIMITS, AUDIENCE_PRESETS, AUDIENCE_TERM_KINDS, AgentActionBus, AgentActionUnsupportedDispatchError, AgentCommandRegistry, AgentDropRegistry, AgentFlightAnimator, AgentLookupRegistry, AgentPayloadOversizeError, AudienceBuilderComponent, AuthService, BilingualFieldComponent, CRON_MODES, CRON_WEEKDAYS, ContextMenuComponent, DEFAULT_AGENT_PAYLOAD_LIMITS, DEFAULT_FLY_THEME_MODE, DS_BASELINE_LOCALES, DashboardKpiComponent, DialogResult, ENTITY_LINK_LAUNCHER, EntityLookupComponent, FLYOS_LAUNCH_EVENT, FLYOS_LAUNCH_REQUEST_EVENT, FLYOS_REMOTE_ROUTE_EVENT, FLY_ACCENT_PROPERTY, FLY_ADMIN_ROLES, FLY_CHUNK_RELOAD_FLAG, FLY_COMPACT_THRESHOLD, FLY_COUNTRIES, FLY_CURRENCIES_BRIEF_ENDPOINT, FLY_CURRENCY_DEFAULT_ENDPOINT, FLY_EMOJI_BY_ID, FLY_EMOJI_CATEGORIES, FLY_EMOJI_CATEGORY_BY_ID, FLY_EMOJI_DEFAULT_CATEGORIES, FLY_EMOJI_PACK, FLY_EMOJI_PACK_MISSING_MESSAGE, FLY_EMOJI_PREVIEW_COUNT, FLY_EMPTY_VALUE, FLY_LOCALE_CATALOG, FLY_MAGIC_BAR_EVENT, FLY_MAGIC_BAR_ICONS, FLY_MAGIC_BAR_STORE_KEY, FLY_NF_CACHE_HEAL_FLAG, FLY_RELOAD_DEBOUNCE_MS, FLY_RELOAD_REARM_MS, FLY_REMOTE_BASE_PATH, FLY_REMOTE_CONTEXT_EVENT, FLY_REMOTE_CONTEXT_STORE_KEY, FLY_REMOTE_ROUTES, FLY_SCAN_PENDING_DEFAULT_DELAY_MS, FLY_SCAN_PENDING_MAX_RETRIES, FLY_SCAN_PENDING_STATUS, FLY_SEARCH_DEBOUNCE_MS, FLY_SKIN_TONES, FLY_STANDALONE_AUTH_CONFIG, FLY_THEME_MODE_IDS, FLY_VIEWPORT_IS_MOBILE, FLY_WINDOW_BREADCRUMBS_EVENT, FLY_WINDOW_BREADCRUMBS_STORE_KEY, FLY_WINDOW_HELP_HINT_EVENT, FLY_WINDOW_PAGE_TITLE_EVENT, FlyAchievementProgressComponent, FlyActionMenuComponent, FlyActionStackComponent, FlyAgentDraggableDirective, FlyAnimatedEmojiComponent, FlyAppHomeComponent, FlyAppTopbarComponent, FlyAppUnavailableComponent, FlyAuthLiveRefresh, FlyBadgeWallComponent, FlyBlockUiComponent, FlyBreadcrumbComponent, FlyButtonComponent, FlyBytesPipe, FlyCaptchaComponent, FlyCardActionsComponent, FlyCardBodyComponent, FlyCardComponent, FlyCardFooterComponent, FlyCardGridComponent, FlyCardMediaComponent, FlyCardMetaComponent, FlyCardStatusComponent, FlyCardTitleComponent, FlyCellDirective, FlyCheckboxComponent, FlyChipComponent, FlyChunkReloadErrorHandler, FlyClickOutsideDirective, FlyCollapsibleComponent, FlyCommentThreadComponent, FlyCompactNumberPipe, FlyConfirmDialogComponent, FlyControlDirective, FlyCronBuilderComponent, FlyCurrencyCatalogService, FlyCurrencySelectorComponent, FlyDataTableComponent, FlyDatePipe, FlyDateTimePipe, FlyDebouncer, FlyDecimalNumberPipe, FlyDeepLinkPrefetchService, FlyDetailCardComponent, FlyDetailShellComponent, FlyDrawerComponent, FlyDurationPipe, FlyDynamicFormComponent, FlyEmojiPickerComponent, FlyFieldComponent, FlyFileDownloadService, FlyFileUploadComponent, FlyFilterPanelComponent, FlyFormBannerComponent, FlyFormFooterComponent, FlyFormGridComponent, FlyFormSectionComponent, FlyFullNumberPipe, FlyGanttComponent, FlyHubClient, FlyIconButtonComponent, FlyIfAdminDirective, FlyIfRoleDirective, FlyImageUploadComponent, FlyLeaderboardComponent, FlyMagicActionsComponent, FlyMetaListComponent, FlyModalComponent, FlyModerationQueueComponent, FlyModuleIconDirective, FlyMoneyPipe, FlyPagerComponent, FlyPaginationComponent, FlyPeoplePickerComponent, FlyPointsTierComponent, FlyProgressComponent, FlyRelativeTimePipe, FlyRemoteContextService, FlyRemoteRouter, FlyRemoteRouterOutletComponent, FlySearchInputComponent, FlySectionHeaderComponent, FlySecureSrcDirective, FlySegmentedComponent, FlySelectComponent, FlySkeletonComponent, FlySliderComponent, FlySparklineComponent, FlySpinnerComponent, FlyStandaloneAuthCallbackComponent, FlyStandaloneAuthService, FlyStandaloneMagicActionsComponent, FlyStateMessageComponent, FlyDynamicFormComponent as FlySurveyFormComponent, FlyTabComponent, FlyTabsComponent, FlyTagsInputComponent, FlyThemeService, FlyTimePipe, FlyToastService, FlyToggleComponent, FlyTooltipDirective, FlyTreeNavComponent, FlyTypeaheadComponent, FlyUserDirectoryService, FlyWindowHelpService, FlyWindowTitleService, FlyosPendingLaunchesGlobalKey, FlyosShellHandlesLaunchRequestsGlobalKey, FlyosShellOwnsHistoryGlobalKey, GANTT_DEPENDENCY_TYPES, GANTT_ROW_TINT_ALPHA, GANTT_ZOOMS, I18nService, LAUNCH_CONTEXT, MAGIC_BAR_SEARCH_WIDTH_DEFAULT, MAGIC_BAR_SEARCH_WIDTH_MAX, MAGIC_BAR_SEARCH_WIDTH_MIN, MagicBarRegistry, MessageBoxButtons, MessageBoxComponent, MessageBoxIcon, MessageBoxService, MockAuthService, NOVA_PRIORITY_TONE, NOVA_STATUS_TONE, NOVA_TONE_FALLBACK, OverlayStack, PRESENCE_COLORS, PageHeaderComponent, RTL_LOCALE_SET, SHARE_ORG_CHART_SYSTEM_KEY_APPS, SHARE_ORG_CHART_SYSTEM_KEY_DEFAULT, SHARE_PANEL_DEFAULT_FILE_LEVELS, STATE_DEFAULT_ICONS, STATE_DEFAULT_MESSAGE_KEYS, STEP_UP_CODE, STEP_UP_REAUTH_HANDLER, STEP_UP_RETURN_KEY, SUPPORTED_AGENT_PAYLOAD_VERSIONS, SharePanelComponent, SourceAppResolver, StandaloneWindowManagerService, StatusBadgeComponent, StepUpService, ToastHostComponent, TranslatePipe, WINDOW_DATA, WINDOW_HELP_HINT, WindowBreadcrumbsRegistry, WindowManagerService, applySkinTone, applySuggestion, ariaSort, buildCron, canConfirm, canGoNext$1 as canGoNext, canGoPrev$1 as canGoPrev, captureFocus, clampMagicBarSearchWidth, clampPage$1 as clampPage, clampSliderValue, connectRemoteLaunch, enterActivatesNatively, filterGroup, filterSuggestions, findLocaleByDialect, findLocaleByPrefix, firstEnabledIndex, firstModuleIndex, flattenModules, flyAdminGuard, flyApiErrorMessage, flyCaptchaBase64Utf8, flyCaptchaBuildToken, flyCaptchaSha256Hex, flyCompactNumber, flyDebounced, flyDecimalNumber, flyDownloadBlob, flyDuration, flyEmojiFor, flyEmojiIsTonable, flyEmojiPackUrl, flyExportFileName, flyFirstEnabledIndex, flyFormatBytes, flyFormatDate, flyFormatDateTime, flyFormatMoney, flyFormatTime, flyFullNumber, flyNextEnabledIndex, flyNextSegmentIndex, flyRelativeTime, flyResolveActiveTab, flyRoleGuard, flyScanPendingDelayMs, flyScanRetry, flySignedCompact, flySkinToneLabelKey, flyStandaloneAuthGuard, flyStandaloneAuthInterceptor, flyToDateOnly, flyToPage, flyUnwrap, flyUnwrapCurrencies, flyUnwrapCurrency, flyUnwrapLenient, hasAnyRoleIn, healNativeFederationCacheOnce, initialExpanded, isAccentColor, isChunkLoadError, isFlyAuthStorePopulated, isNativeFederationCacheError, isRtlLocale, isRtlLocaleEntry, isValidCron, isValidSingleField, loadRemoteStyles, magicBarOwnerKey, magicBarSearchText, matchFlyRoutePattern, nextEnabledIndex, nextModuleIndex, nextSegmentIndex, nextSort, normalizeFlyTheme, normalizeRoles, novaPriorityToneVar, novaStatusToneVar, overlayStack, canGoNext as pagerCanGoNext, canGoPrev as pagerCanGoPrev, clampPage as pagerClampPage, parseCron, parseStepUpChallenge, parseTags, prefetchRemoteStyles, presenceColorFor, printConsoleSecurityWarning, provideFlyChunkReloadRecovery, provideFlyEmojiPack, provideFlyStandaloneAuth, reloadOnceForChunkError, requestAppLaunch, resolveActiveModule, resolveActiveSection, resolveActiveTab, resolveCellValue, resolveStateIcon, resolveStateMessageKey, restoreFocus, sameTags, sectionHintKey, sectionLayout, sliderTrackGradient, sparklinePath, sparklinePoints, stepUpInterceptor, stepUpReturnKey, trimAgentPayload, trimAgentString, unloadRemoteStyles, utf8ByteLength, validateAgentPayload, warnIfEmbeddedSessionMissing };
|
|
26071
26528
|
//# sourceMappingURL=flyos-design-system.mjs.map
|