@zambon-dev/library 1.3.2 → 1.4.1

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/CHANGELOG.md CHANGED
@@ -23,6 +23,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
23
 
24
24
  ### ⚠ Breaking Changes / Migration
25
25
 
26
+ ## [1.4.1] - 2026-09-08
27
+
28
+ - Maintenance release (no consumer-facing changes were documented).
29
+
30
+ ## [1.4.0] - 2026-09-08
31
+
32
+ ### Added
33
+
34
+ - **Sidebar menu items can now open an external destination instead of an internal route.**
35
+ `SidebarMenu` gained an optional `openMode` (`SidebarMenuOpenMode`): `Internal` (the default),
36
+ `ExternalNewTab`, or `ExternalEmbedded`. Read it through the exported
37
+ `toSidebarMenuOpenMode(menu.openMode)` helper rather than comparing the field directly — the
38
+ value is deserialized straight from your menu endpoint, so the helper accepts the mode as a
39
+ camelCase string, as a case-insensitive variant of it, or as the enum ordinal an ASP.NET Core
40
+ enum property serializes to. Anything unrecognized — an omitted field from a backend that
41
+ predates this feature, `null`, or a mode a newer version adds — degrades to `Internal`, so a
42
+ menu you have not touched behaves exactly as it does today.
43
+
44
+ - **`SidebarService.menuExternalUrlSelected`** — a separate output carrying the items whose mode
45
+ is not `Internal`. They are deliberately **not** announced on `menuUrlSelected`, which keeps its
46
+ existing contract: the URL it carries is always an Angular route. Acting on external items needs
47
+ `@zambon-dev/shared` (which opens the browser tab or the embedded view), or your own subscription
48
+ to the new output. Until then an external item highlights and does nothing, rather than sending
49
+ `https://…` to the router.
50
+
51
+ - **`SidebarConfigs.externalLinkText`** — the tooltip on items that open in a new browser tab.
52
+ Defaults to `Opens in a new browser tab` and, like `errorText` and `loadingText`, is rendered
53
+ as-is rather than through the translate pipe, so pass an already-localized string.
54
+
55
+ ### Changed
56
+
57
+ - **Selection now follows the open mode.** An item that opens in a new browser tab is treated as an
58
+ action rather than a destination: it takes no selection pill and, importantly, does not clear the
59
+ selection of the view the user is still looking at. Items that open embedded become real
60
+ application tabs and keep today’s selection behaviour exactly. A parent that also carries a URL
61
+ still expands, so it cannot become impossible to open.
62
+
63
+ - **Items that open in a new browser tab show a trailing outbound glyph.** It sits in the same
64
+ absolutely-positioned slot as the parent chevron, so a long label still ellipsises inside the
65
+ anchor and the collapsed rail is unchanged, and it fades in with the sidebar like the other
66
+ affordances. Embedded items get no glyph — they stay inside the application. Items with no
67
+ `openMode`, or with `openMode: Internal`, render exactly as before.
68
+
69
+ ### ⚠ Breaking Changes / Migration
70
+
71
+ None. `openMode` is optional, so a backend that does not send it produces today’s behaviour, and
72
+ `menuUrlSelected` is unchanged for internal items. If you subscribe to `menuUrlSelected` yourself
73
+ and want external items too, add a subscription to `menuExternalUrlSelected`.
74
+
26
75
  ## [1.3.2] - 2026-07-30
27
76
 
28
77
  ### Added
@@ -68,6 +117,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
68
117
  only the last one saved just that row, because `onSaveClick` validates the current selection only,
69
118
  so the earlier rows silently vanished on refresh. `newData()`'s values are now registered when the
70
119
  row is added.
120
+ - **`lib-catalog-select` no longer renders an empty caption after a form reset.** `FormService.resetForm()`
121
+ clears every control and then patches the model back, so the component receives the value it already
122
+ held. It skipped refreshing the caption in that case, on the assumption that an unchanged value means
123
+ the caption is still on screen — but the reset had emptied the display control a moment earlier. The
124
+ field therefore rendered blank whenever the stored value equalled the control's initial value, which
125
+ is why it only ever showed on the entry the form defaults to: a `nonNullable` control declared as
126
+ `new FormControl(0)` bound to an enum whose first member is `0`, for instance. Values differing from
127
+ the initial one refreshed normally and always looked right, which made the fault read like a
128
+ translation or data problem rather than a reset one. The caption is now rewritten whenever it has
129
+ drifted from the selected entry, so the value alone no longer decides. Selects backed by a
130
+ `searchEndpoint` are unaffected, since their caption comes from the server-side selection rather than
131
+ `entriesList`. No consumer changes are required.
71
132
 
72
133
  ### ⚠ Breaking Changes / Migration
73
134
 
@@ -155,7 +216,9 @@ config options and the `getUserProfile()` method still exist but are no longer c
155
216
  available via [GitHub Releases](https://github.com/RicardoZambon/ZLibraries/releases) and the
156
217
  `library-v*` tags.
157
218
 
158
- [Unreleased]: https://github.com/RicardoZambon/ZLibraries/compare/library-v1.3.2...HEAD
219
+ [Unreleased]: https://github.com/RicardoZambon/ZLibraries/compare/library-v1.4.1...HEAD
220
+ [1.4.1]: https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.4.1
221
+ [1.4.0]: https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.4.0
159
222
  [1.3.2]: https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.3.2
160
223
  [1.3.1]: https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.3.1
161
224
  [1.2.0]: https://github.com/RicardoZambon/ZLibraries/releases/tag/library-v1.2.0
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Component, inject, Injectable, EventEmitter, InjectionToken, Input, Output, KeyValueDiffers, ViewContainerRef, TemplateRef, ElementRef, HostListener, ViewChild, ChangeDetectorRef, ChangeDetectionStrategy, NgZone, Directive, ContentChildren, Pipe, NgModule } from '@angular/core';
3
- import { Subject, ReplaySubject, take, throwError, tap, map, takeUntil, filter, debounceTime, startWith, pairwise, Observable, forkJoin, switchMap, of, delay } from 'rxjs';
3
+ import { Subject, ReplaySubject, take, throwError, tap, map, takeUntil, filter, debounceTime, startWith, pairwise, Observable, forkJoin, switchMap, of, delay, merge } from 'rxjs';
4
4
  import { Overlay, OverlayPositionBuilder } from '@angular/cdk/overlay';
5
5
  import { TemplatePortal } from '@angular/cdk/portal';
6
6
  import * as i1 from '@angular/common';
@@ -974,6 +974,8 @@ const SIDEBAR_CONFIGS = new InjectionToken('', {
974
974
  });
975
975
  class SidebarConfigs {
976
976
  errorText = 'Error';
977
+ /** Tooltip for items that open in a new browser tab. Rendered as-is, like {@link errorText}. */
978
+ externalLinkText = 'Opens in a new browser tab';
977
979
  loadingText = 'Loading';
978
980
  logoCollapsedPath;
979
981
  logoExpandedPath;
@@ -990,6 +992,14 @@ class SidebarMenu {
990
992
  id = 0;
991
993
  label = '';
992
994
  isSelected = false;
995
+ /**
996
+ * How {@link url} is opened. An absent value means {@link SidebarMenuOpenMode.Internal}.
997
+ *
998
+ * Left optional and without an initializer on purpose: the constructor is an `Object.assign`,
999
+ * which would overwrite a default with the `null` a nullable backend column serializes to.
1000
+ * Read it through `toSidebarMenuOpenMode(menu.openMode)` instead of comparing it directly.
1001
+ */
1002
+ openMode;
993
1003
  parent = null;
994
1004
  region;
995
1005
  url;
@@ -998,11 +1008,62 @@ class SidebarMenu {
998
1008
  }
999
1009
  }
1000
1010
 
1011
+ /**
1012
+ * How a {@link SidebarMenu}'s `url` is opened when the item is clicked.
1013
+ *
1014
+ * Read the value off a menu through {@link toSidebarMenuOpenMode} rather than comparing the
1015
+ * field directly: it crosses HTTP, so it can arrive as a string, as a number, as `null`, or not
1016
+ * at all.
1017
+ */
1018
+ var SidebarMenuOpenMode;
1019
+ (function (SidebarMenuOpenMode) {
1020
+ /** `url` is an internal Angular route and opens an application tab. The default. */
1021
+ SidebarMenuOpenMode["Internal"] = "internal";
1022
+ /** `url` is an absolute http(s) address and opens in a new browser tab. */
1023
+ SidebarMenuOpenMode["ExternalNewTab"] = "externalNewTab";
1024
+ /** `url` is an absolute http(s) address and opens embedded in an application tab. */
1025
+ SidebarMenuOpenMode["ExternalEmbedded"] = "externalEmbedded";
1026
+ })(SidebarMenuOpenMode || (SidebarMenuOpenMode = {}));
1027
+ /** Ordinals matching the mode column stored by the backend, for a numeric payload. */
1028
+ const ORDINAL_MODES = [
1029
+ SidebarMenuOpenMode.Internal,
1030
+ SidebarMenuOpenMode.ExternalNewTab,
1031
+ SidebarMenuOpenMode.ExternalEmbedded,
1032
+ ];
1033
+ /**
1034
+ * Parses the wire value of `SidebarMenu.openMode`.
1035
+ *
1036
+ * Deliberately a parser rather than a `?? Internal` fallback, because the field is deserialized
1037
+ * straight from each application's menu endpoint and its shape is not guaranteed:
1038
+ *
1039
+ * - a **number** is read as the backend's enum ordinal, which is what an ASP.NET Core enum
1040
+ * property serializes to unless that backend registers a string converter;
1041
+ * - a **string** is matched case-insensitively, so `'externalNewTab'` and `'ExternalNewTab'`
1042
+ * both work;
1043
+ * - anything else — an omitted field from a backend that predates this feature, `null`, or a
1044
+ * mode introduced by a newer one — degrades to {@link SidebarMenuOpenMode.Internal}.
1045
+ *
1046
+ * Degrading to `Internal` keeps an unrecognized mode behaving the way menus behaved before
1047
+ * external destinations existed, instead of producing an item that does nothing when clicked.
1048
+ */
1049
+ function toSidebarMenuOpenMode(value) {
1050
+ if (typeof value === 'number') {
1051
+ return ORDINAL_MODES[value] ?? SidebarMenuOpenMode.Internal;
1052
+ }
1053
+ if (typeof value === 'string') {
1054
+ const normalized = value.toLowerCase();
1055
+ return ORDINAL_MODES.find((mode) => mode.toLowerCase() === normalized)
1056
+ ?? SidebarMenuOpenMode.Internal;
1057
+ }
1058
+ return SidebarMenuOpenMode.Internal;
1059
+ }
1060
+
1001
1061
  class SidebarService {
1002
1062
  //#region ViewChilds, Inputs, Outputs
1003
1063
  childrenFailed = new EventEmitter();
1004
1064
  childrenLoading = new EventEmitter();
1005
1065
  childrenInitialized = new EventEmitter();
1066
+ menuExternalUrlSelected = new EventEmitter();
1006
1067
  menuUrlSelected = new EventEmitter();
1007
1068
  selectionChanged = new EventEmitter();
1008
1069
  //#endregion
@@ -1049,6 +1110,19 @@ class SidebarService {
1049
1110
  this.loadChildren(menu);
1050
1111
  }
1051
1112
  const hasUrl = (menu.url?.length ?? 0) > 0;
1113
+ const openMode = toSidebarMenuOpenMode(menu.openMode);
1114
+ const isExternal = hasUrl && openMode !== SidebarMenuOpenMode.Internal;
1115
+ // External destinations are announced on their own emitter, so a consumer that only knows
1116
+ // menuUrlSelected never receives an absolute URL it would hand to the Angular router.
1117
+ const urlSelected = isExternal ? this.menuExternalUrlSelected : this.menuUrlSelected;
1118
+ // Opening a new browser tab is an action, not a destination: the user stays on the view
1119
+ // already on screen, so the item must neither take the selection nor clear that view's.
1120
+ // Parents still fall through, so a parent that carries a URL keeps expanding instead of
1121
+ // becoming impossible to open.
1122
+ if (isExternal && openMode === SidebarMenuOpenMode.ExternalNewTab && menu.childCount === 0) {
1123
+ urlSelected.emit(menu);
1124
+ return;
1125
+ }
1052
1126
  // In case we're deselecting the menu because a child was selected, we need to keep track of the original selection state.
1053
1127
  const isSelected = menu.isSelected;
1054
1128
  if (!!this.selectedMenu && this.selectedMenu !== menu) {
@@ -1058,12 +1132,12 @@ class SidebarService {
1058
1132
  this.selectedMenu = menu;
1059
1133
  this.selectMenu(menu);
1060
1134
  if (hasUrl) {
1061
- this.menuUrlSelected.emit(menu);
1135
+ urlSelected.emit(menu);
1062
1136
  }
1063
1137
  }
1064
1138
  else if (hasUrl) {
1065
1139
  // Menu with a URL is already selected — navigate again instead of toggling off.
1066
- this.menuUrlSelected.emit(menu);
1140
+ urlSelected.emit(menu);
1067
1141
  }
1068
1142
  else {
1069
1143
  this.selectedMenu = null;
@@ -1608,7 +1682,7 @@ class CatalogSelectComponent extends BaseComponent {
1608
1682
  .pipe(takeUntil(this.destroy$))
1609
1683
  .subscribe((value) => {
1610
1684
  this.syncFormControlsEnabledDisabled();
1611
- if (this.selectedValue === value) {
1685
+ if (!this.shouldRefreshDisplay(value)) {
1612
1686
  return;
1613
1687
  }
1614
1688
  this.selectedValue = value;
@@ -1618,8 +1692,7 @@ class CatalogSelectComponent extends BaseComponent {
1618
1692
  }
1619
1693
  }
1620
1694
  else {
1621
- const display = this.entriesDataSource.filter((entry) => entry.value === value)[0]?.display ?? '';
1622
- this.displayControl?.setValue(display, { emitEvent: false });
1695
+ this.displayControl?.setValue(this.resolveDisplay(value), { emitEvent: false });
1623
1696
  }
1624
1697
  });
1625
1698
  // Initial sync: if the form control already has a value (e.g., the model was loaded
@@ -1898,16 +1971,36 @@ class CatalogSelectComponent extends BaseComponent {
1898
1971
  this.showNoResultsMessage = false;
1899
1972
  this.showMinimumCharactersMessage = false;
1900
1973
  }
1974
+ resolveDisplay(value) {
1975
+ return this.entriesDataSource.filter((entry) => entry.value === value)[0]?.display ?? '';
1976
+ }
1901
1977
  syncDisplayFromCurrentValue() {
1902
1978
  const currentValue = this.formControl?.value;
1903
- if (currentValue == null || this.selectedValue === currentValue) {
1979
+ if (currentValue == null || !this.shouldRefreshDisplay(currentValue)) {
1904
1980
  return;
1905
1981
  }
1906
1982
  this.selectedValue = currentValue;
1907
1983
  if (!this.hasSearchEndpoint) {
1908
- const display = this.entriesDataSource.filter((entry) => entry.value === currentValue)[0]?.display ?? '';
1909
- this.displayControl?.setValue(display, { emitEvent: false });
1984
+ this.displayControl?.setValue(this.resolveDisplay(currentValue), { emitEvent: false });
1985
+ }
1986
+ }
1987
+ /**
1988
+ * Decides whether the display control has to be written again.
1989
+ *
1990
+ * The value on its own is not enough. Resetting a form clears the display control and then
1991
+ * patches the same value back, so an unchanged value can still leave the display empty. That
1992
+ * only happens when the stored value equals the control's initial value, which is why it used
1993
+ * to show up on the default entry alone.
1994
+ */
1995
+ shouldRefreshDisplay(value) {
1996
+ if (this.selectedValue !== value) {
1997
+ return true;
1998
+ }
1999
+ if (this.hasSearchEndpoint) {
2000
+ return false;
1910
2001
  }
2002
+ const display = this.resolveDisplay(value);
2003
+ return display.length > 0 && this.displayControl?.value !== display;
1911
2004
  }
1912
2005
  syncFormControlsEnabledDisabled() {
1913
2006
  if (this.formControl.enabled !== this.displayControl.enabled) {
@@ -3123,6 +3216,7 @@ class SidebarItemComponent extends BaseComponent {
3123
3216
  isSelected = false;
3124
3217
  _childHeight = 0;
3125
3218
  changeDetectorRef = inject(ChangeDetectorRef);
3219
+ sidebarConfigs = inject(SIDEBAR_CONFIGS);
3126
3220
  sidebarService = inject(SidebarService);
3127
3221
  //#endregion
3128
3222
  //#region Properties
@@ -3132,6 +3226,9 @@ class SidebarItemComponent extends BaseComponent {
3132
3226
  get areChildrenLoaded() {
3133
3227
  return this.isParent && (this.menu.children?.length ?? 0) > 0;
3134
3228
  }
3229
+ get externalLinkText() {
3230
+ return this.sidebarConfigs.externalLinkText;
3231
+ }
3135
3232
  get isActive() {
3136
3233
  return this.sidebarService.isActive;
3137
3234
  }
@@ -3141,6 +3238,10 @@ class SidebarItemComponent extends BaseComponent {
3141
3238
  get isParent() {
3142
3239
  return this.menu.childCount > 0;
3143
3240
  }
3241
+ get opensInNewBrowserTab() {
3242
+ return (this.menu.url?.length ?? 0) > 0
3243
+ && toSidebarMenuOpenMode(this.menu.openMode) === SidebarMenuOpenMode.ExternalNewTab;
3244
+ }
3144
3245
  get hasIcon() {
3145
3246
  return !!this.menu.icon && this.menu.icon.length > 0;
3146
3247
  }
@@ -3216,7 +3317,7 @@ class SidebarItemComponent extends BaseComponent {
3216
3317
  this._childHeight = this.getChildHeight(this.menu);
3217
3318
  }
3218
3319
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3219
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: SidebarItemComponent, isStandalone: true, selector: "lib-sidebar-item", inputs: { isLast: "isLast", level: "level", menu: "menu" }, host: { properties: { "class.active": "isActive", "class.expanded": "!isCollapsed", "class.first-level": "level === 0", "class.last-child": "isLast", "class.selected": "isSelected" } }, viewQueries: [{ propertyName: "menuContainer", first: true, predicate: ["menuContainer"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<li>\n <div #menuContainer\n [class.failed]=\"hasFailed\"\n [class.loading]=\"isLoading\"\n [class.parent]=\"isParent\"\n [class.selected]=\"isSelected\"\n (click)=\"onSelectItem()\">\n <a>\n <i *ngIf=\"hasIcon && level === 0\" class=\"fa-solid fa-fw {{ menu.icon }}\"></i>\n <span>{{ menu.label | translate }}</span>\n </a>\n\n <div class=\"loading\" *ngIf=\"isParent\">\n <svg class=\"animate-spin text-white\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </div>\n </div>\n\n <ul *ngIf=\"isParent\"\n [class.open]=\"isSelected\"\n [ngStyle]=\"{\n 'height.px': childHeight,\n 'max-height.px': childHeight\n }\">\n <lib-sidebar-item *ngFor=\"let child of menu.children; trackBy: trackByFn; let last = last\"\n [menu]=\"child\"\n [level]=\"level+1\"\n [isLast]=\"last\">\n </lib-sidebar-item>\n </ul>\n</li>", styles: ["@charset \"UTF-8\";:host{position:relative;display:block}@media (max-width: 767px){:host:not(.active) li>ul{height:0px!important}}@media (min-width: 768px){:host:not(.expanded):not(.active) li>ul{height:0px!important}:host.expanded li>div{margin-inline:.5rem}:host.expanded li>div.parent:not(.loading):after{opacity:1}:host.expanded li>div.parent a{padding-right:2rem}:host.expanded li>div.parent.loading .loading{opacity:1!important}:host.expanded li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.expanded li>div a span{opacity:1}}:host.active li>div{margin-inline:.5rem}:host.active li>div.parent:not(.loading):after{opacity:1}:host.active li>div.parent a{padding-right:2rem}:host.active li>div.parent.loading .loading{opacity:1!important}:host.active li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.active li>div a span{opacity:1}:host.expanded li .selected:not(.loading).parent:after,:host.active li .selected:not(.loading).parent:after{--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}li{position:relative;height:100%}li>div{position:relative;overflow:hidden;height:var(--sidebar-item-height);line-height:var(--sidebar-item-height);border-radius:var(--sidebar-item-radius);transition-property:margin,background-color;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration)}li>div:hover{background-color:var(--sidebar-item-hover-bg)}li>div.selected{background-color:var(--sidebar-item-selected-bg);color:var(--sidebar-item-selected-text)}li>div.parent:after{position:absolute;top:0;right:.75rem;width:10px;font-weight:800;transition-property:opacity,transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;font-family:\"Font Awesome 6 Free\";transition-duration:var(--sidebar-animation-duration);will-change:opacity,transform}li>div.parent:not(.failed):after{--tw-content: \"\\f104\";content:var(--tw-content)}li>div.parent.failed:after{right:1rem;--tw-content: \"\\f071\";content:var(--tw-content)}li>div a{display:block;height:100%;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.75rem;padding-right:.75rem;text-align:left;width:var(--sidebar-expanded-width)}li>div a i{margin-right:.5rem;font-size:1.25rem;line-height:1.75rem;transition-property:font-size,line-height,margin-left;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:var(--sidebar-icon-collapsed-margin);transition-duration:var(--sidebar-animation-duration);will-change:font-size,line-height,margin-left}li>div a span{text-overflow:ellipsis;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading{position:absolute;top:0;right:.55rem;height:100%;width:1rem;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading svg{height:100%;width:100%}li>ul{overflow:hidden;transition-property:height,max-height;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:height,max-height}li>ul a{padding-left:1.25rem}:host(:not(.first-level)) li>div>a{position:relative;padding-left:1.5rem}:host(:not(.first-level)) li>div>a:before{content:\"\";position:absolute;left:.6rem;top:0;bottom:0;width:0;border-left:1px solid var(--sidebar-tree-line)}:host(:not(.first-level)) li>div>a:after{content:\"\";position:absolute;left:.6rem;top:calc(50% - .55rem);width:.6rem;height:.55rem;border-left:1px solid var(--sidebar-tree-line);border-bottom:1px solid var(--sidebar-tree-line);border-bottom-left-radius:.4rem}:host(.last-child:not(.first-level)) li>div>a:before{bottom:calc(50% + .4rem)}\n"], dependencies: [{ kind: "component", type: SidebarItemComponent, selector: "lib-sidebar-item", inputs: ["isLast", "level", "menu"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
3320
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: SidebarItemComponent, isStandalone: true, selector: "lib-sidebar-item", inputs: { isLast: "isLast", level: "level", menu: "menu" }, host: { properties: { "class.active": "isActive", "class.expanded": "!isCollapsed", "class.first-level": "level === 0", "class.last-child": "isLast", "class.selected": "isSelected" } }, viewQueries: [{ propertyName: "menuContainer", first: true, predicate: ["menuContainer"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<li>\n <div #menuContainer\n [class.external]=\"opensInNewBrowserTab\"\n [class.failed]=\"hasFailed\"\n [class.loading]=\"isLoading\"\n [class.parent]=\"isParent\"\n [class.selected]=\"isSelected\"\n (click)=\"onSelectItem()\">\n <a [attr.title]=\"opensInNewBrowserTab ? externalLinkText : null\">\n <i *ngIf=\"hasIcon && level === 0\" class=\"fa-solid fa-fw {{ menu.icon }}\"></i>\n <span>{{ menu.label | translate }}</span>\n </a>\n\n <div class=\"loading\" *ngIf=\"isParent\">\n <svg class=\"animate-spin text-white\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </div>\n </div>\n\n <ul *ngIf=\"isParent\"\n [class.open]=\"isSelected\"\n [ngStyle]=\"{\n 'height.px': childHeight,\n 'max-height.px': childHeight\n }\">\n <lib-sidebar-item *ngFor=\"let child of menu.children; trackBy: trackByFn; let last = last\"\n [menu]=\"child\"\n [level]=\"level+1\"\n [isLast]=\"last\">\n </lib-sidebar-item>\n </ul>\n</li>", styles: ["@charset \"UTF-8\";:host{position:relative;display:block}@media (max-width: 767px){:host:not(.active) li>ul{height:0px!important}}@media (min-width: 768px){:host:not(.expanded):not(.active) li>ul{height:0px!important}:host.expanded li>div{margin-inline:.5rem}:host.expanded li>div.external:after{opacity:1}:host.expanded li>div.external a{padding-right:2rem}:host.expanded li>div.parent:not(.loading):after{opacity:1}:host.expanded li>div.parent a{padding-right:2rem}:host.expanded li>div.parent.loading .loading{opacity:1!important}:host.expanded li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.expanded li>div a span{opacity:1}}:host.active li>div{margin-inline:.5rem}:host.active li>div.external:after{opacity:1}:host.active li>div.external a{padding-right:2rem}:host.active li>div.parent:not(.loading):after{opacity:1}:host.active li>div.parent a{padding-right:2rem}:host.active li>div.parent.loading .loading{opacity:1!important}:host.active li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.active li>div a span{opacity:1}:host.expanded li .selected:not(.loading).parent:after,:host.active li .selected:not(.loading).parent:after{--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}li{position:relative;height:100%}li>div{position:relative;overflow:hidden;height:var(--sidebar-item-height);line-height:var(--sidebar-item-height);border-radius:var(--sidebar-item-radius);transition-property:margin,background-color;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration)}li>div:hover{background-color:var(--sidebar-item-hover-bg)}li>div.selected{background-color:var(--sidebar-item-selected-bg);color:var(--sidebar-item-selected-text)}li>div.external:after{position:absolute;top:0;right:.75rem;width:10px;font-size:.75rem;line-height:1rem;font-weight:800;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;--tw-content: \"\\f08e\";content:var(--tw-content);font-family:\"Font Awesome 6 Free\";transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div.parent:after{position:absolute;top:0;right:.75rem;width:10px;font-weight:800;transition-property:opacity,transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;font-family:\"Font Awesome 6 Free\";transition-duration:var(--sidebar-animation-duration);will-change:opacity,transform}li>div.parent:not(.failed):after{--tw-content: \"\\f104\";content:var(--tw-content)}li>div.parent.failed:after{right:1rem;--tw-content: \"\\f071\";content:var(--tw-content)}li>div a{display:block;height:100%;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.75rem;padding-right:.75rem;text-align:left;width:var(--sidebar-expanded-width)}li>div a i{margin-right:.5rem;font-size:1.25rem;line-height:1.75rem;transition-property:font-size,line-height,margin-left;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:var(--sidebar-icon-collapsed-margin);transition-duration:var(--sidebar-animation-duration);will-change:font-size,line-height,margin-left}li>div a span{text-overflow:ellipsis;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading{position:absolute;top:0;right:.55rem;height:100%;width:1rem;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading svg{height:100%;width:100%}li>ul{overflow:hidden;transition-property:height,max-height;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:height,max-height}li>ul a{padding-left:1.25rem}:host(:not(.first-level)) li>div>a{position:relative;padding-left:1.5rem}:host(:not(.first-level)) li>div>a:before{content:\"\";position:absolute;left:.6rem;top:0;bottom:0;width:0;border-left:1px solid var(--sidebar-tree-line)}:host(:not(.first-level)) li>div>a:after{content:\"\";position:absolute;left:.6rem;top:calc(50% - .55rem);width:.6rem;height:.55rem;border-left:1px solid var(--sidebar-tree-line);border-bottom:1px solid var(--sidebar-tree-line);border-bottom-left-radius:.4rem}:host(.last-child:not(.first-level)) li>div>a:before{bottom:calc(50% + .4rem)}\n"], dependencies: [{ kind: "component", type: SidebarItemComponent, selector: "lib-sidebar-item", inputs: ["isLast", "level", "menu"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
3220
3321
  }
3221
3322
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SidebarItemComponent, decorators: [{
3222
3323
  type: Component,
@@ -3231,7 +3332,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImpor
3231
3332
  '[class.first-level]': 'level === 0',
3232
3333
  '[class.last-child]': 'isLast',
3233
3334
  '[class.selected]': 'isSelected',
3234
- }, template: "<li>\n <div #menuContainer\n [class.failed]=\"hasFailed\"\n [class.loading]=\"isLoading\"\n [class.parent]=\"isParent\"\n [class.selected]=\"isSelected\"\n (click)=\"onSelectItem()\">\n <a>\n <i *ngIf=\"hasIcon && level === 0\" class=\"fa-solid fa-fw {{ menu.icon }}\"></i>\n <span>{{ menu.label | translate }}</span>\n </a>\n\n <div class=\"loading\" *ngIf=\"isParent\">\n <svg class=\"animate-spin text-white\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </div>\n </div>\n\n <ul *ngIf=\"isParent\"\n [class.open]=\"isSelected\"\n [ngStyle]=\"{\n 'height.px': childHeight,\n 'max-height.px': childHeight\n }\">\n <lib-sidebar-item *ngFor=\"let child of menu.children; trackBy: trackByFn; let last = last\"\n [menu]=\"child\"\n [level]=\"level+1\"\n [isLast]=\"last\">\n </lib-sidebar-item>\n </ul>\n</li>", styles: ["@charset \"UTF-8\";:host{position:relative;display:block}@media (max-width: 767px){:host:not(.active) li>ul{height:0px!important}}@media (min-width: 768px){:host:not(.expanded):not(.active) li>ul{height:0px!important}:host.expanded li>div{margin-inline:.5rem}:host.expanded li>div.parent:not(.loading):after{opacity:1}:host.expanded li>div.parent a{padding-right:2rem}:host.expanded li>div.parent.loading .loading{opacity:1!important}:host.expanded li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.expanded li>div a span{opacity:1}}:host.active li>div{margin-inline:.5rem}:host.active li>div.parent:not(.loading):after{opacity:1}:host.active li>div.parent a{padding-right:2rem}:host.active li>div.parent.loading .loading{opacity:1!important}:host.active li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.active li>div a span{opacity:1}:host.expanded li .selected:not(.loading).parent:after,:host.active li .selected:not(.loading).parent:after{--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}li{position:relative;height:100%}li>div{position:relative;overflow:hidden;height:var(--sidebar-item-height);line-height:var(--sidebar-item-height);border-radius:var(--sidebar-item-radius);transition-property:margin,background-color;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration)}li>div:hover{background-color:var(--sidebar-item-hover-bg)}li>div.selected{background-color:var(--sidebar-item-selected-bg);color:var(--sidebar-item-selected-text)}li>div.parent:after{position:absolute;top:0;right:.75rem;width:10px;font-weight:800;transition-property:opacity,transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;font-family:\"Font Awesome 6 Free\";transition-duration:var(--sidebar-animation-duration);will-change:opacity,transform}li>div.parent:not(.failed):after{--tw-content: \"\\f104\";content:var(--tw-content)}li>div.parent.failed:after{right:1rem;--tw-content: \"\\f071\";content:var(--tw-content)}li>div a{display:block;height:100%;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.75rem;padding-right:.75rem;text-align:left;width:var(--sidebar-expanded-width)}li>div a i{margin-right:.5rem;font-size:1.25rem;line-height:1.75rem;transition-property:font-size,line-height,margin-left;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:var(--sidebar-icon-collapsed-margin);transition-duration:var(--sidebar-animation-duration);will-change:font-size,line-height,margin-left}li>div a span{text-overflow:ellipsis;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading{position:absolute;top:0;right:.55rem;height:100%;width:1rem;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading svg{height:100%;width:100%}li>ul{overflow:hidden;transition-property:height,max-height;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:height,max-height}li>ul a{padding-left:1.25rem}:host(:not(.first-level)) li>div>a{position:relative;padding-left:1.5rem}:host(:not(.first-level)) li>div>a:before{content:\"\";position:absolute;left:.6rem;top:0;bottom:0;width:0;border-left:1px solid var(--sidebar-tree-line)}:host(:not(.first-level)) li>div>a:after{content:\"\";position:absolute;left:.6rem;top:calc(50% - .55rem);width:.6rem;height:.55rem;border-left:1px solid var(--sidebar-tree-line);border-bottom:1px solid var(--sidebar-tree-line);border-bottom-left-radius:.4rem}:host(.last-child:not(.first-level)) li>div>a:before{bottom:calc(50% + .4rem)}\n"] }]
3335
+ }, template: "<li>\n <div #menuContainer\n [class.external]=\"opensInNewBrowserTab\"\n [class.failed]=\"hasFailed\"\n [class.loading]=\"isLoading\"\n [class.parent]=\"isParent\"\n [class.selected]=\"isSelected\"\n (click)=\"onSelectItem()\">\n <a [attr.title]=\"opensInNewBrowserTab ? externalLinkText : null\">\n <i *ngIf=\"hasIcon && level === 0\" class=\"fa-solid fa-fw {{ menu.icon }}\"></i>\n <span>{{ menu.label | translate }}</span>\n </a>\n\n <div class=\"loading\" *ngIf=\"isParent\">\n <svg class=\"animate-spin text-white\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </div>\n </div>\n\n <ul *ngIf=\"isParent\"\n [class.open]=\"isSelected\"\n [ngStyle]=\"{\n 'height.px': childHeight,\n 'max-height.px': childHeight\n }\">\n <lib-sidebar-item *ngFor=\"let child of menu.children; trackBy: trackByFn; let last = last\"\n [menu]=\"child\"\n [level]=\"level+1\"\n [isLast]=\"last\">\n </lib-sidebar-item>\n </ul>\n</li>", styles: ["@charset \"UTF-8\";:host{position:relative;display:block}@media (max-width: 767px){:host:not(.active) li>ul{height:0px!important}}@media (min-width: 768px){:host:not(.expanded):not(.active) li>ul{height:0px!important}:host.expanded li>div{margin-inline:.5rem}:host.expanded li>div.external:after{opacity:1}:host.expanded li>div.external a{padding-right:2rem}:host.expanded li>div.parent:not(.loading):after{opacity:1}:host.expanded li>div.parent a{padding-right:2rem}:host.expanded li>div.parent.loading .loading{opacity:1!important}:host.expanded li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.expanded li>div a span{opacity:1}}:host.active li>div{margin-inline:.5rem}:host.active li>div.external:after{opacity:1}:host.active li>div.external a{padding-right:2rem}:host.active li>div.parent:not(.loading):after{opacity:1}:host.active li>div.parent a{padding-right:2rem}:host.active li>div.parent.loading .loading{opacity:1!important}:host.active li>div a i{margin-left:0;font-size:1.125rem;line-height:1.75rem}:host.active li>div a span{opacity:1}:host.expanded li .selected:not(.loading).parent:after,:host.active li .selected:not(.loading).parent:after{--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}li{position:relative;height:100%}li>div{position:relative;overflow:hidden;height:var(--sidebar-item-height);line-height:var(--sidebar-item-height);border-radius:var(--sidebar-item-radius);transition-property:margin,background-color;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration)}li>div:hover{background-color:var(--sidebar-item-hover-bg)}li>div.selected{background-color:var(--sidebar-item-selected-bg);color:var(--sidebar-item-selected-text)}li>div.external:after{position:absolute;top:0;right:.75rem;width:10px;font-size:.75rem;line-height:1rem;font-weight:800;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;--tw-content: \"\\f08e\";content:var(--tw-content);font-family:\"Font Awesome 6 Free\";transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div.parent:after{position:absolute;top:0;right:.75rem;width:10px;font-weight:800;transition-property:opacity,transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);opacity:0;font-family:\"Font Awesome 6 Free\";transition-duration:var(--sidebar-animation-duration);will-change:opacity,transform}li>div.parent:not(.failed):after{--tw-content: \"\\f104\";content:var(--tw-content)}li>div.parent.failed:after{right:1rem;--tw-content: \"\\f071\";content:var(--tw-content)}li>div a{display:block;height:100%;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.75rem;padding-right:.75rem;text-align:left;width:var(--sidebar-expanded-width)}li>div a i{margin-right:.5rem;font-size:1.25rem;line-height:1.75rem;transition-property:font-size,line-height,margin-left;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);margin-left:var(--sidebar-icon-collapsed-margin);transition-duration:var(--sidebar-animation-duration);will-change:font-size,line-height,margin-left}li>div a span{text-overflow:ellipsis;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading{position:absolute;top:0;right:.55rem;height:100%;width:1rem;opacity:0;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:opacity}li>div .loading svg{height:100%;width:100%}li>ul{overflow:hidden;transition-property:height,max-height;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:var(--sidebar-animation-duration);will-change:height,max-height}li>ul a{padding-left:1.25rem}:host(:not(.first-level)) li>div>a{position:relative;padding-left:1.5rem}:host(:not(.first-level)) li>div>a:before{content:\"\";position:absolute;left:.6rem;top:0;bottom:0;width:0;border-left:1px solid var(--sidebar-tree-line)}:host(:not(.first-level)) li>div>a:after{content:\"\";position:absolute;left:.6rem;top:calc(50% - .55rem);width:.6rem;height:.55rem;border-left:1px solid var(--sidebar-tree-line);border-bottom:1px solid var(--sidebar-tree-line);border-bottom-left-radius:.4rem}:host(.last-child:not(.first-level)) li>div>a:before{bottom:calc(50% + .4rem)}\n"] }]
3235
3336
  }], ctorParameters: () => [], propDecorators: { menuContainer: [{
3236
3337
  type: ViewChild,
3237
3338
  args: ['menuContainer']
@@ -3305,7 +3406,7 @@ class SidebarComponent extends BaseComponent {
3305
3406
  this.updateShouldActivate();
3306
3407
  }
3307
3408
  ngOnInit() {
3308
- this.sidebarService.menuUrlSelected
3409
+ merge(this.sidebarService.menuUrlSelected, this.sidebarService.menuExternalUrlSelected)
3309
3410
  .pipe(takeUntil(this.destroy$))
3310
3411
  .subscribe((_menu) => this.deactivate());
3311
3412
  this.sidebarService.loadRoot()
@@ -3849,5 +3950,5 @@ class DateValidators {
3849
3950
  * Generated bundle index. Do not edit.
3850
3951
  */
3851
3952
 
3852
- export { BaseComponent, BaseDataset, CatalogSelectComponent, CatalogService, DataGridComponent, DataGridConfigsProvider, DataGridDataset, DataGridRowComponent, DataProviderService, DateHelpers, DateValidators, EnumTranslatePipe, FormGroupComponent, FormGroupService, FormInputComponent, FormInputGroupComponent, FormService, GridConfigsProvider, GridDataset, GroupAccordionComponent, GroupContainerComponent, GroupScrollSpyComponent, GuidHelper, LibraryModule, ModalComponent, MultiEditorComponent, MultiEditorDataset, MultiSelectComponent, MultiSelectResultDataset, MultiSelectResultGridComponent, ReplaceManyPipe, ReplacePipe, RibbonButtonComponent, RibbonComponent, RibbonGroupChild, RibbonGroupComponent, RouterFormatter, SIDEBAR_CONFIGS, ScrollSpyDirective, SidebarComponent, SidebarConfigs, SidebarMenu, SidebarService };
3953
+ export { BaseComponent, BaseDataset, CatalogSelectComponent, CatalogService, DataGridComponent, DataGridConfigsProvider, DataGridDataset, DataGridRowComponent, DataProviderService, DateHelpers, DateValidators, EnumTranslatePipe, FormGroupComponent, FormGroupService, FormInputComponent, FormInputGroupComponent, FormService, GridConfigsProvider, GridDataset, GroupAccordionComponent, GroupContainerComponent, GroupScrollSpyComponent, GuidHelper, LibraryModule, ModalComponent, MultiEditorComponent, MultiEditorDataset, MultiSelectComponent, MultiSelectResultDataset, MultiSelectResultGridComponent, ReplaceManyPipe, ReplacePipe, RibbonButtonComponent, RibbonComponent, RibbonGroupChild, RibbonGroupComponent, RouterFormatter, SIDEBAR_CONFIGS, ScrollSpyDirective, SidebarComponent, SidebarConfigs, SidebarMenu, SidebarMenuOpenMode, SidebarService, toSidebarMenuOpenMode };
3853
3954
  //# sourceMappingURL=zambon-dev-library.mjs.map