@dflat-core/web 1.7.2075 → 1.7.2077

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.
@@ -2610,6 +2610,15 @@ function toAdditionalQuery(groups) {
2610
2610
  function additionalQueryOf(condition) {
2611
2611
  return toAdditionalQuery([{ Conditions: [condition] }]);
2612
2612
  }
2613
+ function viewPanelOf(group) {
2614
+ const entries = Object.entries(group?.Panel ?? {}).filter(([, value]) => value !== void 0);
2615
+ return entries.length ? Object.fromEntries(entries) : null;
2616
+ }
2617
+ function withoutViewPanel(group) {
2618
+ if (!group) return null;
2619
+ const { Panel, ...rest } = group;
2620
+ return normalizeViewFilter(rest);
2621
+ }
2613
2622
  const ViewFilterNoValueOperators = [
2614
2623
  "isnull",
2615
2624
  "notnull",
@@ -2622,7 +2631,8 @@ function normalizeViewFilter(group) {
2622
2631
  if (!group) return null;
2623
2632
  const conditions = (group.Conditions ?? []).filter((c) => !!c?.Property && (ViewFilterNoValueOperators.includes(c.Operator) || (c.Operator === "in" || c.Operator === "notin" ? (c.Values ?? []).length > 0 : (c.Value ?? "") !== "" && (c.Operator !== "between" && c.Operator !== "datebetween" || (c.Value2 ?? "") !== ""))));
2624
2633
  const groups = (group.Groups ?? []).map(normalizeViewFilter).filter((g) => !!g);
2625
- if (!conditions.length && !groups.length) return null;
2634
+ const panel = viewPanelOf(group);
2635
+ if (!conditions.length && !groups.length && !panel) return null;
2626
2636
  return {
2627
2637
  Logic: group.Logic === "or" ? "or" : "and",
2628
2638
  Conditions: conditions.map((c) => ({
@@ -2630,7 +2640,8 @@ function normalizeViewFilter(group) {
2630
2640
  Value: c.Value == null ? null : String(c.Value),
2631
2641
  Value2: c.Value2 == null ? null : String(c.Value2)
2632
2642
  })),
2633
- ...groups.length ? { Groups: groups } : {}
2643
+ ...groups.length ? { Groups: groups } : {},
2644
+ ...panel ? { Panel: panel } : {}
2634
2645
  };
2635
2646
  }
2636
2647
  function toViewQuery(group) {
@@ -2669,6 +2680,8 @@ var FilterContext = class FilterContext {
2669
2680
  this.applyFilterUrl$ = new BehaviorSubject(true);
2670
2681
  this.applyFilterUrlEnabled$ = this.applyFilterUrl$.asObservable();
2671
2682
  this.refresh$ = new Subject();
2683
+ this.viewFilter$ = new BehaviorSubject(null);
2684
+ this.viewFilterEditing$ = new BehaviorSubject(false);
2672
2685
  this.config.defaultFilter$.subscribe((df) => {
2673
2686
  this.setDefaultFilter(df);
2674
2687
  });
@@ -2746,6 +2759,99 @@ var FilterContext = class FilterContext {
2746
2759
  if (query === (this.filter$.value?.ViewQuery ?? null)) return;
2747
2760
  this.setFilter({ ViewQuery: query });
2748
2761
  }
2762
+ setViewFilter(label, filter, canEdit = false) {
2763
+ if (this.viewFilterEditing$.value) this.viewFilterEditing$.next(false);
2764
+ this.viewFilter$.next(filter ? {
2765
+ label,
2766
+ filter,
2767
+ canEdit
2768
+ } : null);
2769
+ }
2770
+ editViewFilter() {
2771
+ if (!this.viewFilter$.value?.canEdit) return;
2772
+ this.viewFilterEditing$.next(true);
2773
+ const viewQuery = this.getViewQuery();
2774
+ if (viewQuery && this.additionalQuery$.value.Rows.length === 0 && this.setAdditionalQueryFromView(viewQuery)) this.setViewQuery(null);
2775
+ }
2776
+ setAdditionalQueryFromView(group) {
2777
+ const rowsOf = (g) => {
2778
+ const groups = g.Groups ?? [];
2779
+ if (g.Logic === "and") return groups.length ? null : [g.Conditions];
2780
+ if (groups.some((s) => s.Logic !== "and" && s.Conditions.length > 1 || (s.Groups ?? []).length > 0)) return null;
2781
+ return [...g.Conditions.map((c) => [c]), ...groups.map((s) => s.Conditions)];
2782
+ };
2783
+ const patternOf = (c) => {
2784
+ const value = c.Value ?? "";
2785
+ if (value === "" || value.includes("*")) return c.Operator === "like" && value !== "" ? value : null;
2786
+ switch (c.Operator) {
2787
+ case "like":
2788
+ case "eq": return value;
2789
+ case "contains": return `*${value}*`;
2790
+ case "startswith": return `${value}*`;
2791
+ case "endswith": return `*${value}`;
2792
+ default: return null;
2793
+ }
2794
+ };
2795
+ const rows = rowsOf(group);
2796
+ if (!rows?.length) return false;
2797
+ const written = [];
2798
+ for (const conditions of rows) {
2799
+ const row = this.newFilterRow();
2800
+ for (const condition of conditions) {
2801
+ const pattern = patternOf(condition);
2802
+ const clause = row.ClausesByPropertyName.get(condition.Property);
2803
+ if (pattern === null || !clause || clause.FilterValue !== void 0) return false;
2804
+ clause.FilterValue = pattern;
2805
+ }
2806
+ written.push(row);
2807
+ }
2808
+ this.additionalQuery$.next({
2809
+ ...this.additionalQuery$.value,
2810
+ Rows: written
2811
+ });
2812
+ this.applyAdditionalQuery();
2813
+ return true;
2814
+ }
2815
+ get lockedPanel() {
2816
+ return this.viewFilterEditing$.value ? {} : this.viewFilter$.value?.filter.Panel ?? {};
2817
+ }
2818
+ getPanelValues(properties) {
2819
+ const current = this.filter$.value;
2820
+ const defaults = this.defaultFilter;
2821
+ const entries = properties.filter((p) => JSON.stringify(current?.[p] ?? null) !== JSON.stringify(defaults?.[p] ?? null)).map((p) => [p, current?.[p] ?? null]);
2822
+ return entries.length ? Object.fromEntries(entries) : null;
2823
+ }
2824
+ setPanelValues(properties, panel) {
2825
+ const defaults = this.defaultFilter;
2826
+ const values = Object.fromEntries(properties.map((p) => [p, defaults?.[p]]));
2827
+ Object.assign(values, panel ?? {});
2828
+ const current = this.filter$.value;
2829
+ if (Object.keys(values).every((p) => JSON.stringify(current?.[p] ?? null) === JSON.stringify(values[p] ?? null))) return;
2830
+ this.setFilter(values);
2831
+ }
2832
+ getAdditionalQueryGroup() {
2833
+ let groups = [];
2834
+ try {
2835
+ const json = this.filter$.value?.AdditionalQuery;
2836
+ groups = json ? JSON.parse(json) : [];
2837
+ } catch {
2838
+ groups = [];
2839
+ }
2840
+ const rows = (Array.isArray(groups) ? groups : []).filter((g) => (g?.Conditions ?? []).length > 0).map((g) => ({
2841
+ Logic: "and",
2842
+ Conditions: g.Conditions.map((c) => ({ ...c }))
2843
+ }));
2844
+ if (!rows.length) return null;
2845
+ return rows.length === 1 ? rows[0] : {
2846
+ Logic: "or",
2847
+ Conditions: [],
2848
+ Groups: rows
2849
+ };
2850
+ }
2851
+ clearAdditionalQuery() {
2852
+ this.additionalQuery$.next({ Rows: [] });
2853
+ if (this.filter$.value?.AdditionalQuery) this.setFilter({ AdditionalQuery: null });
2854
+ }
2749
2855
  setSortList(sorts) {
2750
2856
  const list = (sorts ?? []).filter((s) => !Utilities.IsNil(s?.property) && s.property !== "");
2751
2857
  if (list.length === 0) {
@@ -2883,9 +2989,10 @@ var LayoutContext = class LayoutContext {
2883
2989
  }
2884
2990
  setCardMode(card) {
2885
2991
  this._cardMode$.next(card);
2992
+ this.layoutService?.setCardMode(this.entityKey, card);
2886
2993
  }
2887
2994
  toggleCardMode() {
2888
- this._cardMode$.next(!this._cardMode$.value);
2995
+ this.setCardMode(!this._cardMode$.value);
2889
2996
  }
2890
2997
  updateScreenWidth(width = typeof window !== "undefined" ? window.innerWidth : LayoutContext.NarrowWidth) {
2891
2998
  const narrow = width < LayoutContext.NarrowWidth;
@@ -2967,6 +3074,7 @@ var LayoutContext = class LayoutContext {
2967
3074
  this.isSection$ = this._isSection$.asObservable();
2968
3075
  this._defaultPreferences$ = new BehaviorSubject(null);
2969
3076
  this.defaultPreferences$ = this._defaultPreferences$.asObservable();
3077
+ if (this.layoutService?.getCardMode(this.entityKey)) this._cardMode$.next(true);
2970
3078
  this.selection.hasSelection$.subscribe((res) => {
2971
3079
  if (res) this.setShowPropertyPanel(true);
2972
3080
  else this.setShowPropertyPanel(false);
@@ -4270,6 +4378,19 @@ var LayoutService = class LayoutService {
4270
4378
  getFixedLayoutPreferences(entityKey) {
4271
4379
  return this.fixedLayoutMap.get(entityKey);
4272
4380
  }
4381
+ getCardMode(layoutKey) {
4382
+ return !!this.preferencesMap.get(layoutKey)?.CardMode;
4383
+ }
4384
+ setCardMode(layoutKey, value) {
4385
+ const currentPreferences = this.preferencesMap.get(layoutKey) || this.getDefaultPreferences();
4386
+ this.savePreferences(layoutKey, {
4387
+ ...currentPreferences,
4388
+ CardMode: value
4389
+ });
4390
+ try {
4391
+ this.saveLayoutPreferences();
4392
+ } catch {}
4393
+ }
4273
4394
  setIsVerticalLayout(layoutKey, value) {
4274
4395
  const updatedPreferences = {
4275
4396
  ...this.preferencesMap.get(layoutKey) || this.getDefaultPreferences(),
@@ -14067,6 +14188,8 @@ var CanDeactivateGuard = class CanDeactivateGuard {
14067
14188
  if (!Utilities.IsNil(component) && !Utilities.IsNil(component.isDirty)) {
14068
14189
  if (component.isDirty()) return this.modalService.confirmModal("Vous avez des modifications en cours ! Souhaitez-vous Continuer à faire des modifications, Enregister vos changements, ou bien les Annuler ?", () => component.saveAll(), true);
14069
14190
  }
14191
+ const leave = component?.canLeaveView?.();
14192
+ if (leave !== void 0) return leave;
14070
14193
  return true;
14071
14194
  }
14072
14195
  static {
@@ -16266,18 +16389,35 @@ i0.ɵɵngDeclareClassMetadata({
16266
16389
  }
16267
16390
  });
16268
16391
  var GridViewMenuComponent = class GridViewMenuComponent {
16269
- constructor(cd) {
16392
+ constructor(cd, host) {
16270
16393
  this.cd = cd;
16394
+ this.host = host;
16271
16395
  this.done = new EventEmitter();
16272
16396
  this.mobile = false;
16273
16397
  this.loaded = new EventEmitter();
16274
16398
  this.columnsOpen = false;
16275
16399
  this.search = "";
16276
16400
  this.columnsPosition = {};
16277
- this.cardsOpen = false;
16278
- this.cardsPosition = {};
16279
16401
  this.visibleOrder = [];
16280
16402
  }
16403
+ placeSubmenu(item, height, apply) {
16404
+ const width = GridViewMenuComponent.ColumnsWidth;
16405
+ const left = item.right + width + 4 <= window.innerWidth ? item.right + 2 : Math.max(4, item.left - width - 2);
16406
+ const top = Math.max(4, Math.min(item.top - 5, window.innerHeight - 8 - height));
16407
+ apply({
16408
+ left: left + "px",
16409
+ top: top + "px",
16410
+ width: width + "px"
16411
+ });
16412
+ }
16413
+ refineSubmenu(apply) {
16414
+ setTimeout(() => {
16415
+ const menu = this.host?.nativeElement.querySelector(".gvm-columns");
16416
+ if (!menu || !this.submenuItem) return;
16417
+ this.placeSubmenu(this.submenuItem, menu.offsetHeight, apply);
16418
+ this.cd.markForCheck();
16419
+ });
16420
+ }
16281
16421
  ngOnInit() {
16282
16422
  this.chooser.loadViews().pipe(take$1(1)).subscribe((list) => {
16283
16423
  this.viewList = list ?? {
@@ -16287,6 +16427,7 @@ var GridViewMenuComponent = class GridViewMenuComponent {
16287
16427
  };
16288
16428
  this.cd.markForCheck();
16289
16429
  this.loaded.emit();
16430
+ this.refineSubmenu((position) => this.columnsPosition = position);
16290
16431
  });
16291
16432
  }
16292
16433
  isSelected(id) {
@@ -16310,14 +16451,8 @@ var GridViewMenuComponent = class GridViewMenuComponent {
16310
16451
  event?.stopPropagation();
16311
16452
  const item = event?.currentTarget?.getBoundingClientRect();
16312
16453
  if (item) {
16313
- const width = GridViewMenuComponent.ColumnsWidth, height = GridViewMenuComponent.ColumnsHeight;
16314
- const left = item.right + width + 4 <= window.innerWidth ? item.right + 2 : Math.max(4, item.left - width - 2);
16315
- const top = Math.max(4, Math.min(item.top - 5, window.innerHeight - height - 8));
16316
- this.columnsPosition = {
16317
- left: left + "px",
16318
- top: top + "px",
16319
- width: width + "px"
16320
- };
16454
+ this.submenuItem = item;
16455
+ this.placeSubmenu(item, GridViewMenuComponent.SubmenuHeight, (position) => this.columnsPosition = position);
16321
16456
  }
16322
16457
  if (this.columnsOpen) return;
16323
16458
  this.columnsOpen = true;
@@ -16325,52 +16460,12 @@ var GridViewMenuComponent = class GridViewMenuComponent {
16325
16460
  }
16326
16461
  closeColumns() {
16327
16462
  this.columnsOpen = false;
16328
- this.cardsOpen = false;
16329
- }
16330
- openCards(event) {
16331
- event?.stopPropagation();
16332
- this.columnsOpen = false;
16333
- const item = event?.currentTarget?.getBoundingClientRect();
16334
- if (this.mobile) this.cardsPosition = {};
16335
- else if (item) {
16336
- const width = GridViewMenuComponent.ColumnsWidth, height = 220;
16337
- const left = item.right + width + 4 <= window.innerWidth ? item.right + 2 : Math.max(4, item.left - width - 2);
16338
- const top = Math.max(4, Math.min(item.top - 5, window.innerHeight - height - 8));
16339
- this.cardsPosition = {
16340
- left: left + "px",
16341
- top: top + "px",
16342
- width: width + "px"
16343
- };
16344
- }
16345
- if (this.cardsOpen) return;
16346
- this.cardsOpen = true;
16347
- this.cards = void 0;
16348
- this.chooser.loadCards?.().pipe(take$1(1)).subscribe((cards) => {
16349
- this.cards = cards;
16350
- this.cd.markForCheck();
16351
- });
16352
- }
16353
- closeCards() {
16354
- this.cardsOpen = false;
16355
- }
16356
- linkCard(id) {
16357
- if (!this.cards?.canLink || this.cards.linkedId === id) return;
16358
- this.done.emit();
16359
- this.chooser.linkCard?.(id);
16360
- }
16361
- editFilters() {
16362
- this.done.emit();
16363
- this.chooser.editFilters?.();
16364
- }
16365
- editCard() {
16366
- this.done.emit();
16367
- this.chooser.editCard?.();
16368
16463
  }
16369
16464
  static {
16370
16465
  this.ColumnsWidth = 260;
16371
16466
  }
16372
16467
  static {
16373
- this.ColumnsHeight = 320;
16468
+ this.SubmenuHeight = 320;
16374
16469
  }
16375
16470
  loadColumns() {
16376
16471
  this.columns = void 0;
@@ -16453,7 +16548,7 @@ var GridViewMenuComponent = class GridViewMenuComponent {
16453
16548
  version: "22.2.1",
16454
16549
  ngImport: i0,
16455
16550
  type: GridViewMenuComponent,
16456
- deps: [{ token: i0.ChangeDetectorRef }],
16551
+ deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }],
16457
16552
  target: i0.ɵɵFactoryTarget.Component
16458
16553
  });
16459
16554
  }
@@ -16473,8 +16568,8 @@ var GridViewMenuComponent = class GridViewMenuComponent {
16473
16568
  loaded: "loaded"
16474
16569
  },
16475
16570
  ngImport: i0,
16476
- template: "<li *ngIf=\"!viewList\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n<li *ngFor=\"let view of viewList?.views\" class=\"gvm-item gvm-view\" (mouseenter)=\"closeColumns()\" [class.checked]=\"isSelected(view.id)\"\r\n [title]=\"view.isDefault ? 'Vue par défaut' : 'Vue personnalisée'\" (click)=\"toggleView(view.id)\">\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"isSelected(view.id)\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ view.label }}</span>\r\n <i *ngIf=\"view.isDefault && !viewList?.canEditDefault\" class=\"fa fa-lock gvm-lock\"></i>\r\n</li>\r\n<li *ngIf=\"viewList && !viewList.views.length\" class=\"gvm-info\"><span>Aucune vue</span></li>\r\n\r\n<li class=\"gvm-separator\"></li>\r\n<li *ngIf=\"chooser.create\" class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"createView()\">\r\n <i class=\"fa fa-plus gvm-icon\"></i><span class=\"gvm-label\">Nouvelle vue</span>\r\n</li>\r\n\r\n<!-- Colonnes : sous-menu à côté du menu (à droite, ou à gauche s'il ne tient pas) ; colonnes\r\n disponibles, par ordre alphabétique, cochées si affichées ; recherche par mot-clé. -->\r\n<ng-container *ngIf=\"chooser.loadColumns && !mobile\">\r\n <li class=\"gvm-item\" [class.open]=\"columnsOpen\" (click)=\"openColumns($event)\" (mouseenter)=\"closeCards(); openColumns($event)\">\r\n <i class=\"fa fa-list gvm-icon\"></i><span class=\"gvm-label\">Colonnes</span>\r\n <i class=\"fa fa-caret-right gvm-caret\"></i>\r\n </li>\r\n <li *ngIf=\"columnsOpen\" class=\"gvm-columns\" [ngStyle]=\"columnsPosition\" (click)=\"$event.stopPropagation()\">\r\n <input class=\"gvm-search\" type=\"text\" placeholder=\"Rechercher…\" [(ngModel)]=\"search\" />\r\n <ul class=\"gvm-column-list\">\r\n <li *ngIf=\"!columns\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n <!-- Colonnes affichées, dans l'ordre de la grille : à déplacer par glisser-déposer. -->\r\n <li *ngFor=\"let column of visibleColumns()\" class=\"gvm-item gvm-column checked\" draggable=\"true\"\r\n [class.pending]=\"pendingColumn === column.key\" [class.dragging]=\"dragKey === column.key\"\r\n [class.drop-before]=\"dropTarget?.key === column.key && dropTarget?.before\"\r\n [class.drop-after]=\"dropTarget?.key === column.key && !dropTarget?.before\"\r\n [title]=\"column.key\" (click)=\"toggleColumn(column)\"\r\n (dragstart)=\"onDragStart($event, column)\" (dragover)=\"onDragOver($event, column)\"\r\n (drop)=\"onDrop($event)\" (dragend)=\"onDragEnd()\">\r\n <i class=\"fa fa-bars gvm-grip\" title=\"Déplacer la colonne\"></i>\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"true\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ column.label }}</span>\r\n </li>\r\n <!-- Colonnes non affichées, par ordre alphabétique. -->\r\n <li *ngIf=\"visibleColumns().length && hiddenColumns().length\" class=\"gvm-separator\"></li>\r\n <li *ngFor=\"let column of hiddenColumns()\" class=\"gvm-item gvm-column\"\r\n [class.pending]=\"pendingColumn === column.key\" [title]=\"column.key\" (click)=\"toggleColumn(column)\">\r\n <i class=\"fa fa-bars gvm-grip gvm-grip-empty\"></i>\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"false\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ column.label }}</span>\r\n </li>\r\n <li *ngIf=\"columns && !filteredColumns().length\" class=\"gvm-info\"><span>Aucune colonne</span></li>\r\n </ul>\r\n </li>\r\n</ng-container>\r\n\r\n<!-- Filtres et tri : fenêtre de paramétrage, enregistrable dans la vue. -->\r\n<li *ngIf=\"chooser.editFilters\" class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"editFilters()\">\r\n <i class=\"fa fa-filter gvm-icon\"></i><span class=\"gvm-label\">Filtres et tri…</span>\r\n</li>\r\n\r\n<!-- Carte : vue Carte liée à la vue Grille affichée (cochée), autres vues Carte, personnalisation. -->\r\n<ng-container *ngIf=\"chooser.loadCards\">\r\n <li class=\"gvm-item\" [class.open]=\"cardsOpen\" (click)=\"openCards($event)\" (mouseenter)=\"!mobile && openCards($event)\">\r\n <i class=\"fa fa-th-large gvm-icon\"></i><span class=\"gvm-label\">Carte</span>\r\n <i class=\"fa fa-caret-right gvm-caret\"></i>\r\n </li>\r\n <li *ngIf=\"cardsOpen\" class=\"gvm-columns\" [class.gvm-fullscreen]=\"mobile\" [ngStyle]=\"cardsPosition\" (click)=\"$event.stopPropagation()\">\r\n <!-- Téléphone : sous-menu en plein écran, au-dessus du menu ; la flèche revient au menu. -->\r\n <div *ngIf=\"mobile\" class=\"gvm-fullscreen-header\">\r\n <button type=\"button\" class=\"gvm-back\" title=\"Revenir au menu\" aria-label=\"Revenir au menu\" (click)=\"closeCards()\">\r\n <i class=\"fa fa-arrow-left\"></i>\r\n </button>\r\n <span>Carte</span>\r\n </div>\r\n <ul class=\"gvm-column-list\">\r\n <li *ngIf=\"!cards\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n <li *ngFor=\"let card of cards?.cards\" class=\"gvm-item gvm-column\" [class.checked]=\"cards?.linkedId === card.id\"\r\n [class.disabled]=\"!cards?.canLink\"\r\n [title]=\"cards?.canLink ? (card.isDefault ? 'Carte par défaut' : 'Carte personnalisée') : 'La vue par défaut est en lecture seule : dupliquez-la pour lui associer une autre carte'\"\r\n (click)=\"linkCard(card.id)\">\r\n <input type=\"radio\" tabindex=\"-1\" [checked]=\"cards?.linkedId === card.id\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ card.label }}</span>\r\n </li>\r\n <li *ngIf=\"cards && !cards.cards.length\" class=\"gvm-info\"><span>Aucune carte</span></li>\r\n <li class=\"gvm-separator\"></li>\r\n <li class=\"gvm-item\" (click)=\"editCard()\">\r\n <i class=\"fa fa-pencil gvm-icon\"></i><span class=\"gvm-label\">Personnaliser la carte…</span>\r\n </li>\r\n </ul>\r\n </li>\r\n</ng-container>\r\n\r\n<li class=\"gvm-separator\"></li>\r\n<li class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"editView()\">\r\n <i class=\"fa fa-pencil gvm-icon\"></i><span class=\"gvm-label\">Modifier…</span>\r\n</li>\r\n",
16477
- styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{display:contents}li{list-style:none}.gvm-item{display:flex;align-items:center;gap:8px;padding:5px 12px;white-space:nowrap;cursor:pointer;font-size:12px;color:#4d5a81}.gvm-item:hover,.gvm-item.open{background:#ebf0ff}.gvm-item.checked{font-weight:600}.gvm-item.pending{opacity:.5;cursor:progress}.gvm-item input[type=checkbox]{width:14px;margin:0;pointer-events:none}.gvm-label{flex:1;overflow:hidden;text-overflow:ellipsis}.gvm-icon{width:14px;text-align:center;opacity:.8}.gvm-caret{margin-left:auto;opacity:.6}.gvm-lock{margin-left:auto;padding-left:8px;opacity:.5;font-size:11px}.gvm-info{padding:5px 12px;font-size:12px;color:#9fa9c7;cursor:default}.gvm-separator{height:0;margin:4px 0;border-top:1px solid #ebf0ff}.gvm-columns{position:fixed;z-index:3200;box-sizing:border-box;padding:6px;background:#fff;border:1px solid #DBE4FF;border-radius:5px;box-shadow:0 4px 14px #0003}.gvm-search{width:100%;box-sizing:border-box;height:24px;margin-bottom:4px;padding:2px 6px;border:1px solid #C1D0FD;border-radius:3px;font-size:12px;outline:none}.gvm-search:focus{border-color:#5272d3}.gvm-column-list{max-height:270px;overflow-y:auto;margin:0;padding:0}.gvm-column-list .gvm-item{padding:3px 6px}.gvm-grip{width:10px;opacity:.35;cursor:grab;font-size:10px}.gvm-grip-empty{visibility:hidden}.gvm-column{border-top:2px solid transparent;border-bottom:2px solid transparent}.gvm-column.dragging{opacity:.5}.gvm-column.drop-before{border-top-color:var(--orange-algorima, #f59d0f)}.gvm-column.drop-after{border-bottom-color:var(--orange-algorima, #f59d0f)}.gvm-item.disabled{opacity:.55;cursor:default}.gvm-columns.gvm-fullscreen{inset:0;z-index:3300;display:flex;flex-direction:column;padding:0;border:none;border-radius:0;box-shadow:none}.gvm-columns.gvm-fullscreen .gvm-column-list{flex:1 1 auto;max-height:none;overflow-y:auto;padding:6px 0}.gvm-columns.gvm-fullscreen .gvm-item{padding:12px 18px;font-size:14px}.gvm-fullscreen-header{display:flex;align-items:center;gap:8px;flex:none;height:50px;padding:0 8px;padding-top:env(safe-area-inset-top,0px);background:#4465c9;color:#fff;font-size:15px;font-weight:600}.gvm-back{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border:none;border-radius:5px;background:transparent;color:#fff;font-size:18px;cursor:pointer}\n"],
16571
+ template: "<li *ngIf=\"!viewList\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n<li *ngFor=\"let view of viewList?.views\" class=\"gvm-item gvm-view\" (mouseenter)=\"closeColumns()\" [class.checked]=\"isSelected(view.id)\"\r\n [title]=\"view.isDefault ? 'Vue par défaut' : 'Vue personnalisée'\" (click)=\"toggleView(view.id)\">\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"isSelected(view.id)\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ view.label }}</span>\r\n <i *ngIf=\"view.isDefault && !viewList?.canEditDefault\" class=\"fa fa-lock gvm-lock\"></i>\r\n</li>\r\n<li *ngIf=\"viewList && !viewList.views.length\" class=\"gvm-info\"><span>Aucune vue</span></li>\r\n\r\n<li class=\"gvm-separator\"></li>\r\n<li *ngIf=\"chooser.create\" class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"createView()\">\r\n <i class=\"fa fa-plus gvm-icon\"></i><span class=\"gvm-label\">Nouvelle vue</span>\r\n</li>\r\n\r\n<!-- Colonnes : sous-menu à côté du menu (à droite, ou à gauche s'il ne tient pas) ; colonnes\r\n disponibles, par ordre alphabétique, cochées si affichées ; recherche par mot-clé. -->\r\n<ng-container *ngIf=\"chooser.loadColumns && !mobile\">\r\n <li class=\"gvm-item\" [class.open]=\"columnsOpen\" (click)=\"openColumns($event)\" (mouseenter)=\"openColumns($event)\">\r\n <i class=\"fa fa-list gvm-icon\"></i><span class=\"gvm-label\">Colonnes</span>\r\n <i class=\"fa fa-caret-right gvm-caret\"></i>\r\n </li>\r\n <li *ngIf=\"columnsOpen\" class=\"gvm-columns\" [ngStyle]=\"columnsPosition\" (click)=\"$event.stopPropagation()\">\r\n <input class=\"gvm-search\" type=\"text\" placeholder=\"Rechercher…\" [(ngModel)]=\"search\" />\r\n <ul class=\"gvm-column-list\">\r\n <li *ngIf=\"!columns\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n <!-- Colonnes affichées, dans l'ordre de la grille : à déplacer par glisser-déposer. -->\r\n <li *ngFor=\"let column of visibleColumns()\" class=\"gvm-item gvm-column checked\" draggable=\"true\"\r\n [class.pending]=\"pendingColumn === column.key\" [class.dragging]=\"dragKey === column.key\"\r\n [class.drop-before]=\"dropTarget?.key === column.key && dropTarget?.before\"\r\n [class.drop-after]=\"dropTarget?.key === column.key && !dropTarget?.before\"\r\n [title]=\"column.key\" (click)=\"toggleColumn(column)\"\r\n (dragstart)=\"onDragStart($event, column)\" (dragover)=\"onDragOver($event, column)\"\r\n (drop)=\"onDrop($event)\" (dragend)=\"onDragEnd()\">\r\n <i class=\"fa fa-bars gvm-grip\" title=\"Déplacer la colonne\"></i>\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"true\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ column.label }}</span>\r\n </li>\r\n <!-- Colonnes non affichées, par ordre alphabétique. -->\r\n <li *ngIf=\"visibleColumns().length && hiddenColumns().length\" class=\"gvm-separator\"></li>\r\n <li *ngFor=\"let column of hiddenColumns()\" class=\"gvm-item gvm-column\"\r\n [class.pending]=\"pendingColumn === column.key\" [title]=\"column.key\" (click)=\"toggleColumn(column)\">\r\n <i class=\"fa fa-bars gvm-grip gvm-grip-empty\"></i>\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"false\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ column.label }}</span>\r\n </li>\r\n <li *ngIf=\"columns && !filteredColumns().length\" class=\"gvm-info\"><span>Aucune colonne</span></li>\r\n </ul>\r\n </li>\r\n</ng-container>\r\n\r\n\r\n<li class=\"gvm-separator\"></li>\r\n<li class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"editView()\">\r\n <i class=\"fa fa-pencil gvm-icon\"></i><span class=\"gvm-label\">Modifier…</span>\r\n</li>\r\n",
16572
+ styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{display:contents}li{list-style:none}.gvm-item{display:flex;align-items:center;gap:8px;padding:5px 12px;white-space:nowrap;cursor:pointer;font-size:12px;color:#4d5a81}.gvm-item:hover,.gvm-item.open{background:#ebf0ff}.gvm-item.checked{font-weight:600}.gvm-item.pending{opacity:.5;cursor:progress}.gvm-item input[type=checkbox]{width:14px;margin:0;pointer-events:none}.gvm-label{flex:1;overflow:hidden;text-overflow:ellipsis}.gvm-icon{width:14px;text-align:center;opacity:.8}.gvm-caret{margin-left:auto;opacity:.6}.gvm-lock{margin-left:auto;padding-left:8px;opacity:.5;font-size:11px}.gvm-info{padding:5px 12px;font-size:12px;color:#9fa9c7;cursor:default}.gvm-separator{height:0;margin:4px 0;border-top:1px solid #ebf0ff}.gvm-columns{position:fixed;z-index:3200;box-sizing:border-box;padding:6px;background:#fff;border:1px solid #DBE4FF;border-radius:5px;box-shadow:0 4px 14px #0003}.gvm-search{width:100%;box-sizing:border-box;height:24px;margin-bottom:4px;padding:2px 6px;border:1px solid #C1D0FD;border-radius:3px;font-size:12px;outline:none}.gvm-search:focus{border-color:#5272d3}.gvm-column-list{max-height:270px;overflow-y:auto;margin:0;padding:0}.gvm-column-list .gvm-item{padding:3px 6px}.gvm-grip{width:10px;opacity:.35;cursor:grab;font-size:10px}.gvm-grip-empty{visibility:hidden}.gvm-column{border-top:2px solid transparent;border-bottom:2px solid transparent}.gvm-column.dragging{opacity:.5}.gvm-column.drop-before{border-top-color:var(--orange-algorima, #f59d0f)}.gvm-column.drop-after{border-bottom-color:var(--orange-algorima, #f59d0f)}\n"],
16478
16573
  dependencies: [
16479
16574
  {
16480
16575
  kind: "ngmodule",
@@ -16550,11 +16645,11 @@ i0.ɵɵngDeclareClassMetadata({
16550
16645
  standalone: true,
16551
16646
  imports: [CommonModule, FormsModule],
16552
16647
  changeDetection: ChangeDetectionStrategy.Eager,
16553
- template: "<li *ngIf=\"!viewList\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n<li *ngFor=\"let view of viewList?.views\" class=\"gvm-item gvm-view\" (mouseenter)=\"closeColumns()\" [class.checked]=\"isSelected(view.id)\"\r\n [title]=\"view.isDefault ? 'Vue par défaut' : 'Vue personnalisée'\" (click)=\"toggleView(view.id)\">\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"isSelected(view.id)\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ view.label }}</span>\r\n <i *ngIf=\"view.isDefault && !viewList?.canEditDefault\" class=\"fa fa-lock gvm-lock\"></i>\r\n</li>\r\n<li *ngIf=\"viewList && !viewList.views.length\" class=\"gvm-info\"><span>Aucune vue</span></li>\r\n\r\n<li class=\"gvm-separator\"></li>\r\n<li *ngIf=\"chooser.create\" class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"createView()\">\r\n <i class=\"fa fa-plus gvm-icon\"></i><span class=\"gvm-label\">Nouvelle vue</span>\r\n</li>\r\n\r\n<!-- Colonnes : sous-menu à côté du menu (à droite, ou à gauche s'il ne tient pas) ; colonnes\r\n disponibles, par ordre alphabétique, cochées si affichées ; recherche par mot-clé. -->\r\n<ng-container *ngIf=\"chooser.loadColumns && !mobile\">\r\n <li class=\"gvm-item\" [class.open]=\"columnsOpen\" (click)=\"openColumns($event)\" (mouseenter)=\"closeCards(); openColumns($event)\">\r\n <i class=\"fa fa-list gvm-icon\"></i><span class=\"gvm-label\">Colonnes</span>\r\n <i class=\"fa fa-caret-right gvm-caret\"></i>\r\n </li>\r\n <li *ngIf=\"columnsOpen\" class=\"gvm-columns\" [ngStyle]=\"columnsPosition\" (click)=\"$event.stopPropagation()\">\r\n <input class=\"gvm-search\" type=\"text\" placeholder=\"Rechercher…\" [(ngModel)]=\"search\" />\r\n <ul class=\"gvm-column-list\">\r\n <li *ngIf=\"!columns\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n <!-- Colonnes affichées, dans l'ordre de la grille : à déplacer par glisser-déposer. -->\r\n <li *ngFor=\"let column of visibleColumns()\" class=\"gvm-item gvm-column checked\" draggable=\"true\"\r\n [class.pending]=\"pendingColumn === column.key\" [class.dragging]=\"dragKey === column.key\"\r\n [class.drop-before]=\"dropTarget?.key === column.key && dropTarget?.before\"\r\n [class.drop-after]=\"dropTarget?.key === column.key && !dropTarget?.before\"\r\n [title]=\"column.key\" (click)=\"toggleColumn(column)\"\r\n (dragstart)=\"onDragStart($event, column)\" (dragover)=\"onDragOver($event, column)\"\r\n (drop)=\"onDrop($event)\" (dragend)=\"onDragEnd()\">\r\n <i class=\"fa fa-bars gvm-grip\" title=\"Déplacer la colonne\"></i>\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"true\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ column.label }}</span>\r\n </li>\r\n <!-- Colonnes non affichées, par ordre alphabétique. -->\r\n <li *ngIf=\"visibleColumns().length && hiddenColumns().length\" class=\"gvm-separator\"></li>\r\n <li *ngFor=\"let column of hiddenColumns()\" class=\"gvm-item gvm-column\"\r\n [class.pending]=\"pendingColumn === column.key\" [title]=\"column.key\" (click)=\"toggleColumn(column)\">\r\n <i class=\"fa fa-bars gvm-grip gvm-grip-empty\"></i>\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"false\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ column.label }}</span>\r\n </li>\r\n <li *ngIf=\"columns && !filteredColumns().length\" class=\"gvm-info\"><span>Aucune colonne</span></li>\r\n </ul>\r\n </li>\r\n</ng-container>\r\n\r\n<!-- Filtres et tri : fenêtre de paramétrage, enregistrable dans la vue. -->\r\n<li *ngIf=\"chooser.editFilters\" class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"editFilters()\">\r\n <i class=\"fa fa-filter gvm-icon\"></i><span class=\"gvm-label\">Filtres et tri…</span>\r\n</li>\r\n\r\n<!-- Carte : vue Carte liée à la vue Grille affichée (cochée), autres vues Carte, personnalisation. -->\r\n<ng-container *ngIf=\"chooser.loadCards\">\r\n <li class=\"gvm-item\" [class.open]=\"cardsOpen\" (click)=\"openCards($event)\" (mouseenter)=\"!mobile && openCards($event)\">\r\n <i class=\"fa fa-th-large gvm-icon\"></i><span class=\"gvm-label\">Carte</span>\r\n <i class=\"fa fa-caret-right gvm-caret\"></i>\r\n </li>\r\n <li *ngIf=\"cardsOpen\" class=\"gvm-columns\" [class.gvm-fullscreen]=\"mobile\" [ngStyle]=\"cardsPosition\" (click)=\"$event.stopPropagation()\">\r\n <!-- Téléphone : sous-menu en plein écran, au-dessus du menu ; la flèche revient au menu. -->\r\n <div *ngIf=\"mobile\" class=\"gvm-fullscreen-header\">\r\n <button type=\"button\" class=\"gvm-back\" title=\"Revenir au menu\" aria-label=\"Revenir au menu\" (click)=\"closeCards()\">\r\n <i class=\"fa fa-arrow-left\"></i>\r\n </button>\r\n <span>Carte</span>\r\n </div>\r\n <ul class=\"gvm-column-list\">\r\n <li *ngIf=\"!cards\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n <li *ngFor=\"let card of cards?.cards\" class=\"gvm-item gvm-column\" [class.checked]=\"cards?.linkedId === card.id\"\r\n [class.disabled]=\"!cards?.canLink\"\r\n [title]=\"cards?.canLink ? (card.isDefault ? 'Carte par défaut' : 'Carte personnalisée') : 'La vue par défaut est en lecture seule : dupliquez-la pour lui associer une autre carte'\"\r\n (click)=\"linkCard(card.id)\">\r\n <input type=\"radio\" tabindex=\"-1\" [checked]=\"cards?.linkedId === card.id\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ card.label }}</span>\r\n </li>\r\n <li *ngIf=\"cards && !cards.cards.length\" class=\"gvm-info\"><span>Aucune carte</span></li>\r\n <li class=\"gvm-separator\"></li>\r\n <li class=\"gvm-item\" (click)=\"editCard()\">\r\n <i class=\"fa fa-pencil gvm-icon\"></i><span class=\"gvm-label\">Personnaliser la carte…</span>\r\n </li>\r\n </ul>\r\n </li>\r\n</ng-container>\r\n\r\n<li class=\"gvm-separator\"></li>\r\n<li class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"editView()\">\r\n <i class=\"fa fa-pencil gvm-icon\"></i><span class=\"gvm-label\">Modifier…</span>\r\n</li>\r\n",
16554
- styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{display:contents}li{list-style:none}.gvm-item{display:flex;align-items:center;gap:8px;padding:5px 12px;white-space:nowrap;cursor:pointer;font-size:12px;color:#4d5a81}.gvm-item:hover,.gvm-item.open{background:#ebf0ff}.gvm-item.checked{font-weight:600}.gvm-item.pending{opacity:.5;cursor:progress}.gvm-item input[type=checkbox]{width:14px;margin:0;pointer-events:none}.gvm-label{flex:1;overflow:hidden;text-overflow:ellipsis}.gvm-icon{width:14px;text-align:center;opacity:.8}.gvm-caret{margin-left:auto;opacity:.6}.gvm-lock{margin-left:auto;padding-left:8px;opacity:.5;font-size:11px}.gvm-info{padding:5px 12px;font-size:12px;color:#9fa9c7;cursor:default}.gvm-separator{height:0;margin:4px 0;border-top:1px solid #ebf0ff}.gvm-columns{position:fixed;z-index:3200;box-sizing:border-box;padding:6px;background:#fff;border:1px solid #DBE4FF;border-radius:5px;box-shadow:0 4px 14px #0003}.gvm-search{width:100%;box-sizing:border-box;height:24px;margin-bottom:4px;padding:2px 6px;border:1px solid #C1D0FD;border-radius:3px;font-size:12px;outline:none}.gvm-search:focus{border-color:#5272d3}.gvm-column-list{max-height:270px;overflow-y:auto;margin:0;padding:0}.gvm-column-list .gvm-item{padding:3px 6px}.gvm-grip{width:10px;opacity:.35;cursor:grab;font-size:10px}.gvm-grip-empty{visibility:hidden}.gvm-column{border-top:2px solid transparent;border-bottom:2px solid transparent}.gvm-column.dragging{opacity:.5}.gvm-column.drop-before{border-top-color:var(--orange-algorima, #f59d0f)}.gvm-column.drop-after{border-bottom-color:var(--orange-algorima, #f59d0f)}.gvm-item.disabled{opacity:.55;cursor:default}.gvm-columns.gvm-fullscreen{inset:0;z-index:3300;display:flex;flex-direction:column;padding:0;border:none;border-radius:0;box-shadow:none}.gvm-columns.gvm-fullscreen .gvm-column-list{flex:1 1 auto;max-height:none;overflow-y:auto;padding:6px 0}.gvm-columns.gvm-fullscreen .gvm-item{padding:12px 18px;font-size:14px}.gvm-fullscreen-header{display:flex;align-items:center;gap:8px;flex:none;height:50px;padding:0 8px;padding-top:env(safe-area-inset-top,0px);background:#4465c9;color:#fff;font-size:15px;font-weight:600}.gvm-back{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border:none;border-radius:5px;background:transparent;color:#fff;font-size:18px;cursor:pointer}\n"]
16648
+ template: "<li *ngIf=\"!viewList\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n<li *ngFor=\"let view of viewList?.views\" class=\"gvm-item gvm-view\" (mouseenter)=\"closeColumns()\" [class.checked]=\"isSelected(view.id)\"\r\n [title]=\"view.isDefault ? 'Vue par défaut' : 'Vue personnalisée'\" (click)=\"toggleView(view.id)\">\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"isSelected(view.id)\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ view.label }}</span>\r\n <i *ngIf=\"view.isDefault && !viewList?.canEditDefault\" class=\"fa fa-lock gvm-lock\"></i>\r\n</li>\r\n<li *ngIf=\"viewList && !viewList.views.length\" class=\"gvm-info\"><span>Aucune vue</span></li>\r\n\r\n<li class=\"gvm-separator\"></li>\r\n<li *ngIf=\"chooser.create\" class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"createView()\">\r\n <i class=\"fa fa-plus gvm-icon\"></i><span class=\"gvm-label\">Nouvelle vue</span>\r\n</li>\r\n\r\n<!-- Colonnes : sous-menu à côté du menu (à droite, ou à gauche s'il ne tient pas) ; colonnes\r\n disponibles, par ordre alphabétique, cochées si affichées ; recherche par mot-clé. -->\r\n<ng-container *ngIf=\"chooser.loadColumns && !mobile\">\r\n <li class=\"gvm-item\" [class.open]=\"columnsOpen\" (click)=\"openColumns($event)\" (mouseenter)=\"openColumns($event)\">\r\n <i class=\"fa fa-list gvm-icon\"></i><span class=\"gvm-label\">Colonnes</span>\r\n <i class=\"fa fa-caret-right gvm-caret\"></i>\r\n </li>\r\n <li *ngIf=\"columnsOpen\" class=\"gvm-columns\" [ngStyle]=\"columnsPosition\" (click)=\"$event.stopPropagation()\">\r\n <input class=\"gvm-search\" type=\"text\" placeholder=\"Rechercher…\" [(ngModel)]=\"search\" />\r\n <ul class=\"gvm-column-list\">\r\n <li *ngIf=\"!columns\" class=\"gvm-info\"><span>Chargement…</span></li>\r\n <!-- Colonnes affichées, dans l'ordre de la grille : à déplacer par glisser-déposer. -->\r\n <li *ngFor=\"let column of visibleColumns()\" class=\"gvm-item gvm-column checked\" draggable=\"true\"\r\n [class.pending]=\"pendingColumn === column.key\" [class.dragging]=\"dragKey === column.key\"\r\n [class.drop-before]=\"dropTarget?.key === column.key && dropTarget?.before\"\r\n [class.drop-after]=\"dropTarget?.key === column.key && !dropTarget?.before\"\r\n [title]=\"column.key\" (click)=\"toggleColumn(column)\"\r\n (dragstart)=\"onDragStart($event, column)\" (dragover)=\"onDragOver($event, column)\"\r\n (drop)=\"onDrop($event)\" (dragend)=\"onDragEnd()\">\r\n <i class=\"fa fa-bars gvm-grip\" title=\"Déplacer la colonne\"></i>\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"true\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ column.label }}</span>\r\n </li>\r\n <!-- Colonnes non affichées, par ordre alphabétique. -->\r\n <li *ngIf=\"visibleColumns().length && hiddenColumns().length\" class=\"gvm-separator\"></li>\r\n <li *ngFor=\"let column of hiddenColumns()\" class=\"gvm-item gvm-column\"\r\n [class.pending]=\"pendingColumn === column.key\" [title]=\"column.key\" (click)=\"toggleColumn(column)\">\r\n <i class=\"fa fa-bars gvm-grip gvm-grip-empty\"></i>\r\n <input type=\"checkbox\" tabindex=\"-1\" [checked]=\"false\" (click)=\"$event.preventDefault()\" />\r\n <span class=\"gvm-label\">{{ column.label }}</span>\r\n </li>\r\n <li *ngIf=\"columns && !filteredColumns().length\" class=\"gvm-info\"><span>Aucune colonne</span></li>\r\n </ul>\r\n </li>\r\n</ng-container>\r\n\r\n\r\n<li class=\"gvm-separator\"></li>\r\n<li class=\"gvm-item\" (mouseenter)=\"closeColumns()\" (click)=\"editView()\">\r\n <i class=\"fa fa-pencil gvm-icon\"></i><span class=\"gvm-label\">Modifier…</span>\r\n</li>\r\n",
16649
+ styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{display:contents}li{list-style:none}.gvm-item{display:flex;align-items:center;gap:8px;padding:5px 12px;white-space:nowrap;cursor:pointer;font-size:12px;color:#4d5a81}.gvm-item:hover,.gvm-item.open{background:#ebf0ff}.gvm-item.checked{font-weight:600}.gvm-item.pending{opacity:.5;cursor:progress}.gvm-item input[type=checkbox]{width:14px;margin:0;pointer-events:none}.gvm-label{flex:1;overflow:hidden;text-overflow:ellipsis}.gvm-icon{width:14px;text-align:center;opacity:.8}.gvm-caret{margin-left:auto;opacity:.6}.gvm-lock{margin-left:auto;padding-left:8px;opacity:.5;font-size:11px}.gvm-info{padding:5px 12px;font-size:12px;color:#9fa9c7;cursor:default}.gvm-separator{height:0;margin:4px 0;border-top:1px solid #ebf0ff}.gvm-columns{position:fixed;z-index:3200;box-sizing:border-box;padding:6px;background:#fff;border:1px solid #DBE4FF;border-radius:5px;box-shadow:0 4px 14px #0003}.gvm-search{width:100%;box-sizing:border-box;height:24px;margin-bottom:4px;padding:2px 6px;border:1px solid #C1D0FD;border-radius:3px;font-size:12px;outline:none}.gvm-search:focus{border-color:#5272d3}.gvm-column-list{max-height:270px;overflow-y:auto;margin:0;padding:0}.gvm-column-list .gvm-item{padding:3px 6px}.gvm-grip{width:10px;opacity:.35;cursor:grab;font-size:10px}.gvm-grip-empty{visibility:hidden}.gvm-column{border-top:2px solid transparent;border-bottom:2px solid transparent}.gvm-column.dragging{opacity:.5}.gvm-column.drop-before{border-top-color:var(--orange-algorima, #f59d0f)}.gvm-column.drop-after{border-bottom-color:var(--orange-algorima, #f59d0f)}\n"]
16555
16650
  }]
16556
16651
  }],
16557
- ctorParameters: () => [{ type: i0.ChangeDetectorRef }],
16652
+ ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }],
16558
16653
  propDecorators: {
16559
16654
  chooser: [{
16560
16655
  type: Input,
@@ -16765,11 +16860,17 @@ var ActionPanelComponent = class ActionPanelComponent {
16765
16860
  this.actions = acts;
16766
16861
  });
16767
16862
  this.wrapper = new FormWrapper(this.validationService, this.bus, false);
16768
- combineLatest([this.facade.criteria$, this.facade.config.filterDescriptors$]).pipe(takeUntil$1(this.destroy$)).subscribe(([fltr, fds]) => {
16863
+ combineLatest([
16864
+ this.facade.criteria$,
16865
+ this.facade.config.filterDescriptors$,
16866
+ this.facade.filter.viewFilter$,
16867
+ this.facade.filter.viewFilterEditing$
16868
+ ]).pipe(takeUntil$1(this.destroy$)).subscribe(([fltr, fds]) => {
16769
16869
  const filters = fds.filter((x) => x.PropertyName != "Keywords" && x.PropertyName.toUpperCase() != "ID");
16770
16870
  this.fields = filters;
16771
16871
  this.wrapper.initialize(filters);
16772
16872
  this.wrapper.hookFormEntity(fltr);
16873
+ Object.keys(this.facade.filter.lockedPanel).forEach((p) => this.wrapper.form.get(p)?.disable({ emitEvent: false }));
16773
16874
  });
16774
16875
  this.wrapper.changes$.pipe(takeUntil$1(this.destroy$)).subscribe((evt) => this.handleFilterEvent(evt));
16775
16876
  }
@@ -16794,7 +16895,10 @@ var ActionPanelComponent = class ActionPanelComponent {
16794
16895
  }
16795
16896
  handleFilterEvent(evt) {
16796
16897
  if (evt.type === "reset") {
16797
- this.facade.filter.setFilter(evt.value);
16898
+ this.facade.filter.setFilter({
16899
+ ...evt.value,
16900
+ ...this.facade.filter.lockedPanel
16901
+ });
16798
16902
  return;
16799
16903
  }
16800
16904
  this.wrapper.applyCascadeFrom(evt.field);
@@ -17472,6 +17576,7 @@ var DataGridComponent = class DataGridComponent {
17472
17576
  this.rowRegistry = new RowRegistry();
17473
17577
  this.destroy$ = new Subject();
17474
17578
  this.columns = [];
17579
+ this.appendingPage = false;
17475
17580
  this.selectedIds = /* @__PURE__ */ new Set();
17476
17581
  this.rowForms = /* @__PURE__ */ new Map();
17477
17582
  this.singleLineFields = /* @__PURE__ */ new Map();
@@ -17502,8 +17607,27 @@ var DataGridComponent = class DataGridComponent {
17502
17607
  });
17503
17608
  this.facade.actions.actions$.pipe(filter((acts) => !Utilities.IsNil(acts)), takeUntil(this.destroy$)).subscribe((acts) => this.actions = acts);
17504
17609
  }
17610
+ static {
17611
+ this.ScrollLoadThreshold = 200;
17612
+ }
17613
+ onGridScroll(event) {
17614
+ const container = event.target;
17615
+ const page = this.facade.filter.pageNumber$.value ?? 1;
17616
+ const pages = this.facade.filter.totalPages$.value ?? 1;
17617
+ if (this.appendingPage || page >= pages || container.scrollTop + container.clientHeight < container.scrollHeight - DataGridComponent.ScrollLoadThreshold) return;
17618
+ this.appendingPage = true;
17619
+ this.facade.filter.setPage(page + 1);
17620
+ }
17505
17621
  bindData() {
17506
17622
  this.facade.data.data$.subscribe((res) => {
17623
+ if (this.appendingPage && this.data?.Values?.length) {
17624
+ const shown = new Set(this.data.Values.map((r) => r.Id));
17625
+ res = {
17626
+ ...res,
17627
+ Values: [...this.data.Values, ...(res?.Values ?? []).filter((r) => !shown.has(r.Id))]
17628
+ };
17629
+ }
17630
+ this.appendingPage = false;
17507
17631
  this.data = res;
17508
17632
  this.facade.selection.setCurrentData(res.Values);
17509
17633
  this.rowRegistry.setExpectedCount(this.data?.Values?.length ?? 0);
@@ -18786,7 +18910,7 @@ var DataGridComponent = class DataGridComponent {
18786
18910
  }
18787
18911
  ],
18788
18912
  ngImport: i0,
18789
- template: "<div #datagridContainer [class]=\"this.datagridClass\" tabindex=\"-1\" (keydown)=\"onGridKeyDown($event)\"\r\n (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\" (contextmenu)=\"openEmptyAreaContextMenu($event)\">\r\n <ng-container>\r\n <table class=\"data-grid-datatable\">\r\n <ng-container *ngIf=\"this.facade.style as ctx\">\r\n <ng-container *ngIf=\"this.facade.config.columns$ | async as rcolumns\">\r\n <thead>\r\n <tr>\r\n <th class=\"header row-state\" (click)=\"addFilterQuery()\"></th>\r\n <!-- En-tête : poignée de déplacement à gauche, intitulé, indicateur de tri,\r\n flèche du menu (tri, agrégation) à droite. Le clic ne trie plus. -->\r\n <th #thHeader *ngFor=\"let col of rcolumns;let i = index\"\r\n [attr.id]=\"guid.toString() + '-col-' + col.Property\"\r\n [attr.class]=\"'header' | gridStyle : ctx : { column : col }\"\r\n [style.width.px]=\"colWidth(col)\" [style.min-width.px]=\"colWidth(col)\" [style.max-width.px]=\"colWidth(col)\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\">\r\n <div class=\"header-cell\"\r\n [class.col-dragging]=\"draggedColumn === col.Property\">\r\n <span class=\"header-handle\" title=\"Déplacer la colonne\"\r\n (mousedown)=\"onHeaderMouseDown(col, $event)\">\r\n <i class=\"fa fa-ellipsis-v\"></i>\r\n </span>\r\n <label>{{ getHeaderLabel(col) }}</label>\r\n <i *ngIf=\"this.facade.filter.isAsc(col.Property)\" class=\"fa fa-long-arrow-up header-sort\" title=\"Tri croissant\"></i>\r\n <i *ngIf=\"this.facade.filter.isDesc(col.Property)\" class=\"fa fa-long-arrow-down header-sort\" title=\"Tri décroissant\"></i>\r\n <sup *ngIf=\"this.facade.filter.sortRank(col.Property) > 0\" class=\"header-sort-rank\"\r\n title=\"Rang dans le tri personnalisé\">{{ this.facade.filter.sortRank(col.Property) }}</sup>\r\n <i *ngIf=\"activeAggregations[col.Property]\" class=\"header-agg\" [title]=\"'Agrégation : ' + aggregationLabel(activeAggregations[col.Property])\">Σ</i>\r\n <!-- Dernière colonne suivie de la colonne vide de fin : sa flèche est dans la colonne vide. -->\r\n <button type=\"button\" class=\"header-menu-button\" title=\"Trier, agréger\"\r\n *ngIf=\"!(ctx.enableLastColumnResize && i === rcolumns.length - 1)\"\r\n [class.open]=\"headerMenu?.column.Property === col.Property\"\r\n (click)=\"toggleHeaderMenu(col, $event)\">\r\n <i class=\"fa fa-caret-down\"></i>\r\n </button>\r\n </div>\r\n <!-- Bord droit : largeur de la colonne (tirer), ajustée au contenu (double clic). -->\r\n <span class=\"col-resizer\" title=\"Largeur de la colonne (double clic : ajuster au contenu)\"\r\n (mousedown)=\"startResize(col, $event)\" (dblclick)=\"autoFitColumn(col, $event)\"\r\n (click)=\"$event.stopPropagation()\"></span>\r\n </th>\r\n <th *ngIf=\"ctx.enableLastColumnResize\" #lastColumnHeader class=\"header last-column\"\r\n (contextmenu)=\"openHeaderContextMenu($event, rcolumns[rcolumns.length - 1])\">\r\n <div class=\"header-cell header-cell-end\" *ngIf=\"rcolumns.length > 0\">\r\n <button type=\"button\" class=\"header-menu-button\" title=\"Trier, agréger\"\r\n [class.open]=\"headerMenu?.column.Property === rcolumns[rcolumns.length - 1].Property\"\r\n (click)=\"toggleHeaderMenu(rcolumns[rcolumns.length - 1], $event)\">\r\n <i class=\"fa fa-caret-down\"></i>\r\n </button>\r\n </div>\r\n </th>\r\n\r\n </tr>\r\n <ng-container *ngIf=\"this.facade.filter.additionalQuery$ | async as additionalQuery\">\r\n <tr #thFilterRows *ngFor=\"let filterRow of additionalQuery.Rows;let i = index\" >\r\n <th class=\"filter-header row-state\" (click)=\"removeFilterQuery(i)\"></th>\r\n <th class=\"filter-header\" [ngClass]=\"{disabled: !col.IsFilterEnabled}\" *ngFor=\"let col of rcolumns;let i = index\"\r\n [attr.id]=\"guid.toString() + '-filter-' + i\">\r\n <input *ngIf=\"col.IsFilterEnabled\" type=\"text\"\r\n class=\"filter-header-input\"\r\n name=\"keyword\"\r\n #uniquename=\"ngModel\"\r\n [attr.id]=\"guid.toString() + '-filter-input-' + col.Property\"\r\n [(ngModel)]=\"filterRow.ClausesByPropertyName.get(col.Property).FilterValue\"\r\n (ngModelChange)=\"handleFilterChanged($event, filterRow, col.Property)\"\r\n (change)=\"validateFilterChanged($event, filterRow, col.Property)\"\r\n />\r\n </th>\r\n <ng-container>\r\n <th #lastColumnFilter class=\"filter-header last-column\"></th>\r\n </ng-container>\r\n </tr>\r\n </ng-container>\r\n </thead>\r\n\r\n <tbody>\r\n <ng-container *ngIf=\"this.facade.data\">\r\n <ng-container *ngFor=\"let row of this.data.Values; let r = index;\">\r\n <tr\r\n [class]=\"'alternating' | gridStyle : ctx : { row : row, index : r, maxIndex : this.data.Values.length, hasAggregates: hasAggregates }\"\r\n [class.selected]=\"isRowSelected(row)\"\r\n [attr.id]=\"guid.toString() + '-row-' + row.Id\"\r\n (mousedown)=\"onRowMouseDown(row, $event)\"\r\n (mouseup)=\"onRowMouseUp(row, $event)\"\r\n (click)=\"onRowClick(row, $event)\"\r\n\r\n [rowRef]=\"guid + '-row-' + row.Id\"\r\n >\r\n\r\n <th [attr.class]=\"'rowstate' | gridStyle : ctx : { row : row }\">\r\n <ng-container *ngIf=\"selectionStyler.isModified(row) && !selectionStyler.isInvalid(row) && !selectionStyler.isWarning(row)\">\r\n <div class=\"new-rowstate\"><span>...</span></div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectionStyler.isInvalid(row)\">\r\n <div> <img class=\"error-row-icon\" src=\"assets/images/error.svg\" /> </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectionStyler.isWarning(row) && !selectionStyler.isInvalid(row)\">\r\n <div> <img class=\"error-row-icon\" src=\"assets/images/warning.svg\" /> </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"ctx.displayDetail\">\r\n <button type=\"button\" [attr.id]=\"row.Id + '_button'\" class=\"toggle-button btn\" (click)=\"toggleDetail(row)\">\r\n <i class=\"fa fa-chevron-right fa-custom\"></i>\r\n </button>\r\n </ng-container>\r\n </th>\r\n\r\n <td *ngFor=\"let column of rcolumns; index as i\"\r\n [attr.id]=\"cellId(row, column)\"\r\n [style.max-width.px]=\"colWidth(column)\" [attr.data-sized]=\"colWidth(column) ? '' : null\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\"\r\n [class]=\"'cell' | gridStyle : ctx : { row : row,column : column,index : r }\"\r\n [class.selected]=\"isRowSelected(row)\"\r\n (mousedown)=\"onCellMouseDown(r, i, $event)\"\r\n (mouseenter)=\"onCellMouseEnter(r, i, $event)\"\r\n (click)=\"onCellClick(row, column)\"\r\n (dblclick)=\"onCellDblClick(row, column)\">\r\n\r\n <alg-datagrid-cell #dataGridCellComponent\r\n class=\"gridcell-container\"\r\n [entity]=\"row\"\r\n [isEditable]=\"canEditCell(row, column)\"\r\n [property]=\"column.Property\"\r\n [managed]=\"true\"\r\n [mode]=\"showEditor(row, column) ? 'edit' : 'view'\"\r\n [class.grid-cursor]=\"isCursorCell(row, column)\"\r\n [class.grid-range]=\"isInRange(r, i)\"\r\n (contextmenu)=\"openContextMenu($event, row, column)\">\r\n\r\n <ng-template alg-editMode>\r\n <!-- Éditeur du champ du formulaire qui correspond à la colonne (sinon déduit de la colonne), à la hauteur de la ligne. -->\r\n <alg-field class=\"grid-editor\"\r\n [field]=\"getEditField(column)\"\r\n [form]=\"getRowForm(row)\"\r\n [showLabel]=\"false\"\r\n (propertyChanged)=\"onCellChanged(row, $event)\"\r\n >\r\n <!-- [focusable]=\"cellFocus\" -->\r\n </alg-field>\r\n </ng-template>\r\n <ng-template alg-viewMode>\r\n\r\n <div [ngSwitch]=\"column.Display?.type\" *ngIf=\"!isUndefined(row[column.Property])\">\r\n <div *ngSwitchCase=\"'url'\">\r\n <a [routerLink]=\"[$safeNavigationMigration(column.Display?.baseUrl),row[column.Property]]\">\r\n {{ row.Id || row[column.Property] }}\r\n </a>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'img'\" class=\"image\">\r\n <img *ngIf=\"column.Display?.imagePath?.length == 0\"\r\n [src]=\"(column.Display?.imgBase || '') + (column.Display?.sourceProperty ? row[column.Display?.sourceProperty] : row[column.Property])\" />\r\n\r\n <alg-image *ngIf=\"column.Display?.imagePath?.length != 0\"\r\n [imageUrl]=\"this.getEnumImageUrl(row,column)\" [imageHeight]=\"20\" [imageWidth]=\"20\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'img-file'\" class=\"image\">\r\n\r\n <alg-image\r\n [imageUrl]=\"this.getImageUrl(row,column)\" [wide]=\"true\" [imageHeight]=\"40\" [imageWidth]=\"40\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'wide-img'\" class=\"image\">\r\n <img *ngIf=\"column.Display?.imagePath?.length == 0\"\r\n [src]=\"(column.Display?.imgBase || '') + (column.Display?.sourceProperty ? row[column.Display?.sourceProperty] : row[column.Property])\" />\r\n\r\n <alg-image *ngIf=\"column.Display?.imagePath?.length != 0\" [wide]=\"true\"\r\n [imageUrl]=\"this.getEnumImageUrl(row,column)\" [imageHeight]=\"40\" [imageWidth]=\"40\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <span *ngSwitchCase=\"'date'\">\r\n {{row[column.Property]| date: column.Display?.format || 'dd/MM/yyyy'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'boolean'\">{{row[column.Property] ? 'OUI' : 'NON'}} </span>\r\n\r\n <span *ngSwitchCase=\"'datetime'\">{{row[column.Property] | date: column.Display?.format || 'dd/MM/yyyy HH:mm:ss'}}</span>\r\n\r\n <span *ngSwitchCase=\"'money'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\"\r\n class=\"money\">\r\n {{\r\n row[column.Property]\r\n | currency: column.Display?.currency\r\n || (column.Display?.sourceProperty && row[column.Display?.sourceProperty] ? row[column.Display?.sourceProperty] : false)\r\n || 'EUR' : 'symbol'\r\n\r\n | todefine: $safeNavigationMigration(column.Display?.handleError)\r\n }}\r\n\r\n <div class=\"tool-tip\" *ngIf=\"ctx.showOnToolTip(row,column)\">\r\n {{ctx.showOnToolTip(row,column)}}\r\n </div>\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'percent'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] * $safeNavigationMigration(column.Display?.multiplier) | percent : column.Display?.format\r\n || '1.0-2' | todefine: $safeNavigationMigration(column.Display?.handleError)}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'number'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] * $safeNavigationMigration(column.Display?.multiplier) | number: column.Display?.format\r\n || '1.0-3' | todefine : $safeNavigationMigration(column.Display?.handleError)}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'fixedsizeinteger'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] | tofixedsizeinteger : $safeNavigationMigration(column.Display?.completingCaracter) : $safeNavigationMigration(column.Display?.width) }}\r\n </span>\r\n <span *ngSwitchDefault [innerText]=\"column.Property == 'Id' ? (row[column.Property] < 0 ? '' : row[column.Property]) : row[column.Property]\">\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"isUndefined(row[column.Property])\">\r\n <span *ngIf=\"!isUndefined($safeNavigationMigration(column.Display?.nullCaseText))\" class=\"mw-invalid\">\r\n {{ column.Display?.nullCaseText }}\r\n </span>\r\n\r\n <span *ngIf=\"isUndefined($safeNavigationMigration(column.Display?.nullCaseText))\">\r\n &nbsp;&nbsp;&nbsp;\r\n </span>\r\n </div>\r\n\r\n </ng-template>\r\n\r\n </alg-datagrid-cell>\r\n </td>\r\n <ng-container *ngIf=\"ctx.enableLastColumnResize\">\r\n <!-- Colonne vide de fin : clic droit = menu de la dernière colonne. -->\r\n <td (contextmenu)=\"openContextMenu($event, row, rcolumns[rcolumns.length - 1])\"></td>\r\n </ng-container>\r\n </tr>\r\n </ng-container>\r\n </ng-container>\r\n </tbody>\r\n\r\n <tfoot>\r\n <tr class=\"empty-row\" *ngIf=\"hasAggregates\">\r\n <td></td>\r\n </tr>\r\n <tr class=\"aggregation-row\" *ngIf=\"hasAggregates\">\r\n <td class=\"row-state\"></td>\r\n <td *ngFor=\"let column of rcolumns; index as i\" [attr.class]=\"'cell' | gridStyle : ctx : { column : column, index : i }\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\">\r\n\r\n <ng-container *ngFor=\"let agg of columnAggregates[column.Property]\">\r\n\r\n <span class=\"agg-label\">{{ aggregationLabel(agg.op) }}</span>\r\n <span [ngSwitch]=\"agg.op === 'COUNT' ? 'count' : column.Display?.type\">\r\n\r\n <span *ngSwitchCase=\"'count'\">{{ agg.value | number: '1.0-0' }}</span>\r\n <span *ngSwitchCase=\"'percent'\">{{ agg.value | number: column.Display?.format || '1.0-3' }}</span>\r\n <span *ngSwitchCase=\"'fixedsizeinteger'\">{{ agg.value | number: column.Display?.format || '1.0-3' }}</span>\r\n\r\n <span *ngSwitchCase=\"'money'\" class=\"money\">\r\n {{ agg.value | currency: getColCurrency(column) }}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'date'\">\r\n {{agg.value | date: column.Display?.format || 'dd/MM/yyyy'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'datetime'\">\r\n {{agg.value | date: column.Display?.format || 'dd/MM/yyyy HH:mm:ss'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'number'\">\r\n {{agg.value | number: column.Display?.format || '1.0-3'}}\r\n </span>\r\n\r\n <span *ngSwitchDefault>\r\n {{agg.value}}\r\n </span>\r\n\r\n </span>\r\n\r\n </ng-container>\r\n\r\n </td>\r\n\r\n <ng-container *ngIf=\"ctx.enableLastColumnResize\">\r\n <td class=\"last-column\"></td>\r\n </ng-container>\r\n\r\n </tr>\r\n </tfoot>\r\n </ng-container>\r\n </ng-container>\r\n </table>\r\n </ng-container>\r\n</div>\r\n\r\n\r\n<!-- Emplacement visé par une colonne déplacée : trait orange sur la séparation des deux en-têtes. -->\r\n<div class=\"col-drop-indicator\" *ngIf=\"dropIndicator\"\r\n [ngStyle]=\"{ left: dropIndicator.left + 'px', top: dropIndicator.top + 'px', height: dropIndicator.height + 'px' }\"></div>\r\n\r\n<!-- Menu d'une colonne (flèche de l'en-tête ou clic droit sur une cellule), en trois zones séparées\r\n par un trait : Tri, Actions (celles du panneau des actions), Fonctions (sous-menu « Fonctions »). -->\r\n<div class=\"header-menu\" *ngIf=\"headerMenu\"\r\n [ngStyle]=\"{ top: headerMenu.y + 'px', left: headerMenu.x + 'px', visibility: headerMenu.ready ? 'visible' : 'hidden' }\"\r\n (click)=\"$event.stopPropagation()\" (contextmenu)=\"$event.preventDefault()\">\r\n <ul>\r\n <!-- Tri -->\r\n <li [class.checked]=\"this.facade.filter.isAsc(headerMenu.column.Property)\" (click)=\"sortColumn('asc')\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort-amount-asc\"></i><span>Trier par ordre croissant</span>\r\n </li>\r\n <li [class.checked]=\"this.facade.filter.isDesc(headerMenu.column.Property)\" (click)=\"sortColumn('desc')\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort-amount-desc\"></i><span>Trier par ordre décroissant</span>\r\n </li>\r\n <li [class.checked]=\"this.facade.filter.getSortList().length > 1\" (click)=\"openCustomSort()\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort\"></i><span>Tri personnalisé…</span>\r\n </li>\r\n <!-- Filtre sur la valeur de la cellule (clic droit sur une cellule) : première ligne des filtres composés. -->\r\n <li *ngIf=\"cellFilterValue() !== undefined\" (click)=\"filterOnCell()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-filter\"></i><span>Filtrer sur « {{ cellFilterLabel() }} »</span>\r\n </li>\r\n <!-- Affichage : vues personnalisées (cochée = affichée), puis « Modifier… ». -->\r\n <li *ngIf=\"this.facade.config.viewChooser\" class=\"has-submenu\" [class.open]=\"headerMenu.submenu === 'columns'\"\r\n (mouseenter)=\"setSubmenu('columns')\" (click)=\"setSubmenu('columns')\">\r\n <i class=\"fa fa-columns\"></i><span>Affichage</span><i class=\"fa fa-caret-right submenu-caret\"></i>\r\n\r\n <ul class=\"header-submenu header-submenu-columns\" *ngIf=\"headerMenu.submenu === 'columns'\"\r\n [class.left]=\"headerMenu.submenuLeft\" [class.up]=\"headerMenu.submenuUp\"\r\n (click)=\"$event.stopPropagation()\">\r\n <alg-grid-view-menu [chooser]=\"this.facade.config.viewChooser\" (done)=\"closeViewMenu()\" (loaded)=\"onViewMenuLoaded()\"></alg-grid-view-menu>\r\n </ul>\r\n </li>\r\n <!-- Colonnes figées à gauche, de la première jusqu'à celle-ci. -->\r\n <li [class.checked]=\"isFrozen(columnIndex(headerMenu.column))\" (click)=\"toggleFrozen()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-thumb-tack\"></i>\r\n <span>{{ frozenUntil === columnIndex(headerMenu.column) ? 'Libérer les colonnes' : 'Figer jusqu\\'à cette colonne' }}</span>\r\n </li>\r\n <li *ngIf=\"frozenUntil !== undefined && frozenUntil !== columnIndex(headerMenu.column)\"\r\n (click)=\"unfreezeColumns()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-unlock\"></i><span>Libérer les colonnes</span>\r\n </li>\r\n\r\n <!-- Actions -->\r\n <ng-container *ngIf=\"visibleActions.length > 0\">\r\n <li class=\"separator\"></li>\r\n <ng-container *ngFor=\"let action of visibleActions\">\r\n <ng-container *ngIf=\"{ disabled: (action.disabled$ | async) } as state\">\r\n <li class=\"menu-action\" [class.disabled]=\"state.disabled\"\r\n (click)=\"executeMenuAction(action, state.disabled)\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <img class=\"menu-action-icon\" [src]=\"getActionImageUrl(action.icon)\" [alt]=\"action.title\" />\r\n <span>{{ action.title }}</span>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- Affichage -->\r\n <li class=\"separator\"></li>\r\n <li class=\"has-submenu\" [class.open]=\"headerMenu.submenu === 'display'\"\r\n (mouseenter)=\"setSubmenu('display')\" (click)=\"setSubmenu('display')\">\r\n <i class=\"fa fa-calculator\"></i><span>Fonctions</span><i class=\"fa fa-caret-right submenu-caret\"></i>\r\n\r\n <ul class=\"header-submenu\" *ngIf=\"headerMenu.submenu === 'display'\"\r\n [class.left]=\"headerMenu.submenuLeft\" [class.up]=\"headerMenu.submenuUp\"\r\n (click)=\"$event.stopPropagation()\">\r\n <li *ngFor=\"let op of getAggregationOptions(headerMenu.column)\"\r\n [class.checked]=\"activeAggregations[headerMenu.column.Property] === op\"\r\n (click)=\"aggregateColumn(op)\">\r\n <i class=\"fa fa-check\"></i><span>{{ aggregationLabel(op) }}</span>\r\n </li>\r\n <ng-container *ngIf=\"activeAggregations[headerMenu.column.Property]\">\r\n <li class=\"separator\"></li>\r\n <li (click)=\"aggregateColumn(null)\">\r\n <i class=\"fa fa-times\"></i><span>Ne plus afficher</span>\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ul>\r\n</div>\r\n\r\n",
18913
+ template: "<div #datagridContainer [class]=\"this.datagridClass\" tabindex=\"-1\" (keydown)=\"onGridKeyDown($event)\" (scroll)=\"onGridScroll($event)\"\r\n (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\" (contextmenu)=\"openEmptyAreaContextMenu($event)\">\r\n <ng-container>\r\n <table class=\"data-grid-datatable\">\r\n <ng-container *ngIf=\"this.facade.style as ctx\">\r\n <ng-container *ngIf=\"this.facade.config.columns$ | async as rcolumns\">\r\n <thead>\r\n <tr>\r\n <th class=\"header row-state\" (click)=\"addFilterQuery()\"></th>\r\n <!-- En-tête : poignée de déplacement à gauche, intitulé, indicateur de tri,\r\n flèche du menu (tri, agrégation) à droite. Le clic ne trie plus. -->\r\n <th #thHeader *ngFor=\"let col of rcolumns;let i = index\"\r\n [attr.id]=\"guid.toString() + '-col-' + col.Property\"\r\n [attr.class]=\"'header' | gridStyle : ctx : { column : col }\"\r\n [style.width.px]=\"colWidth(col)\" [style.min-width.px]=\"colWidth(col)\" [style.max-width.px]=\"colWidth(col)\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\">\r\n <div class=\"header-cell\"\r\n [class.col-dragging]=\"draggedColumn === col.Property\">\r\n <span class=\"header-handle\" title=\"Déplacer la colonne\"\r\n (mousedown)=\"onHeaderMouseDown(col, $event)\">\r\n <i class=\"fa fa-ellipsis-v\"></i>\r\n </span>\r\n <label>{{ getHeaderLabel(col) }}</label>\r\n <i *ngIf=\"this.facade.filter.isAsc(col.Property)\" class=\"fa fa-long-arrow-up header-sort\" title=\"Tri croissant\"></i>\r\n <i *ngIf=\"this.facade.filter.isDesc(col.Property)\" class=\"fa fa-long-arrow-down header-sort\" title=\"Tri décroissant\"></i>\r\n <sup *ngIf=\"this.facade.filter.sortRank(col.Property) > 0\" class=\"header-sort-rank\"\r\n title=\"Rang dans le tri personnalisé\">{{ this.facade.filter.sortRank(col.Property) }}</sup>\r\n <i *ngIf=\"activeAggregations[col.Property]\" class=\"header-agg\" [title]=\"'Agrégation : ' + aggregationLabel(activeAggregations[col.Property])\">Σ</i>\r\n <!-- Dernière colonne suivie de la colonne vide de fin : sa flèche est dans la colonne vide. -->\r\n <button type=\"button\" class=\"header-menu-button\" title=\"Trier, agréger\"\r\n *ngIf=\"!(ctx.enableLastColumnResize && i === rcolumns.length - 1)\"\r\n [class.open]=\"headerMenu?.column.Property === col.Property\"\r\n (click)=\"toggleHeaderMenu(col, $event)\">\r\n <i class=\"fa fa-caret-down\"></i>\r\n </button>\r\n </div>\r\n <!-- Bord droit : largeur de la colonne (tirer), ajustée au contenu (double clic). -->\r\n <span class=\"col-resizer\" title=\"Largeur de la colonne (double clic : ajuster au contenu)\"\r\n (mousedown)=\"startResize(col, $event)\" (dblclick)=\"autoFitColumn(col, $event)\"\r\n (click)=\"$event.stopPropagation()\"></span>\r\n </th>\r\n <th *ngIf=\"ctx.enableLastColumnResize\" #lastColumnHeader class=\"header last-column\"\r\n (contextmenu)=\"openHeaderContextMenu($event, rcolumns[rcolumns.length - 1])\">\r\n <div class=\"header-cell header-cell-end\" *ngIf=\"rcolumns.length > 0\">\r\n <button type=\"button\" class=\"header-menu-button\" title=\"Trier, agréger\"\r\n [class.open]=\"headerMenu?.column.Property === rcolumns[rcolumns.length - 1].Property\"\r\n (click)=\"toggleHeaderMenu(rcolumns[rcolumns.length - 1], $event)\">\r\n <i class=\"fa fa-caret-down\"></i>\r\n </button>\r\n </div>\r\n </th>\r\n\r\n </tr>\r\n <ng-container *ngIf=\"this.facade.filter.additionalQuery$ | async as additionalQuery\">\r\n <tr #thFilterRows *ngFor=\"let filterRow of additionalQuery.Rows;let i = index\" >\r\n <th class=\"filter-header row-state\" (click)=\"removeFilterQuery(i)\"></th>\r\n <th class=\"filter-header\" [ngClass]=\"{disabled: !col.IsFilterEnabled}\" *ngFor=\"let col of rcolumns;let i = index\"\r\n [attr.id]=\"guid.toString() + '-filter-' + i\">\r\n <input *ngIf=\"col.IsFilterEnabled\" type=\"text\"\r\n class=\"filter-header-input\"\r\n name=\"keyword\"\r\n #uniquename=\"ngModel\"\r\n [attr.id]=\"guid.toString() + '-filter-input-' + col.Property\"\r\n [(ngModel)]=\"filterRow.ClausesByPropertyName.get(col.Property).FilterValue\"\r\n (ngModelChange)=\"handleFilterChanged($event, filterRow, col.Property)\"\r\n (change)=\"validateFilterChanged($event, filterRow, col.Property)\"\r\n />\r\n </th>\r\n <ng-container>\r\n <th #lastColumnFilter class=\"filter-header last-column\"></th>\r\n </ng-container>\r\n </tr>\r\n </ng-container>\r\n </thead>\r\n\r\n <tbody>\r\n <ng-container *ngIf=\"this.facade.data\">\r\n <ng-container *ngFor=\"let row of this.data.Values; let r = index;\">\r\n <tr\r\n [class]=\"'alternating' | gridStyle : ctx : { row : row, index : r, maxIndex : this.data.Values.length, hasAggregates: hasAggregates }\"\r\n [class.selected]=\"isRowSelected(row)\"\r\n [attr.id]=\"guid.toString() + '-row-' + row.Id\"\r\n (mousedown)=\"onRowMouseDown(row, $event)\"\r\n (mouseup)=\"onRowMouseUp(row, $event)\"\r\n (click)=\"onRowClick(row, $event)\"\r\n\r\n [rowRef]=\"guid + '-row-' + row.Id\"\r\n >\r\n\r\n <th [attr.class]=\"'rowstate' | gridStyle : ctx : { row : row }\">\r\n <ng-container *ngIf=\"selectionStyler.isModified(row) && !selectionStyler.isInvalid(row) && !selectionStyler.isWarning(row)\">\r\n <div class=\"new-rowstate\"><span>...</span></div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectionStyler.isInvalid(row)\">\r\n <div> <img class=\"error-row-icon\" src=\"assets/images/error.svg\" /> </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectionStyler.isWarning(row) && !selectionStyler.isInvalid(row)\">\r\n <div> <img class=\"error-row-icon\" src=\"assets/images/warning.svg\" /> </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"ctx.displayDetail\">\r\n <button type=\"button\" [attr.id]=\"row.Id + '_button'\" class=\"toggle-button btn\" (click)=\"toggleDetail(row)\">\r\n <i class=\"fa fa-chevron-right fa-custom\"></i>\r\n </button>\r\n </ng-container>\r\n </th>\r\n\r\n <td *ngFor=\"let column of rcolumns; index as i\"\r\n [attr.id]=\"cellId(row, column)\"\r\n [style.max-width.px]=\"colWidth(column)\" [attr.data-sized]=\"colWidth(column) ? '' : null\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\"\r\n [class]=\"'cell' | gridStyle : ctx : { row : row,column : column,index : r }\"\r\n [class.selected]=\"isRowSelected(row)\"\r\n (mousedown)=\"onCellMouseDown(r, i, $event)\"\r\n (mouseenter)=\"onCellMouseEnter(r, i, $event)\"\r\n (click)=\"onCellClick(row, column)\"\r\n (dblclick)=\"onCellDblClick(row, column)\">\r\n\r\n <alg-datagrid-cell #dataGridCellComponent\r\n class=\"gridcell-container\"\r\n [entity]=\"row\"\r\n [isEditable]=\"canEditCell(row, column)\"\r\n [property]=\"column.Property\"\r\n [managed]=\"true\"\r\n [mode]=\"showEditor(row, column) ? 'edit' : 'view'\"\r\n [class.grid-cursor]=\"isCursorCell(row, column)\"\r\n [class.grid-range]=\"isInRange(r, i)\"\r\n (contextmenu)=\"openContextMenu($event, row, column)\">\r\n\r\n <ng-template alg-editMode>\r\n <!-- Éditeur du champ du formulaire qui correspond à la colonne (sinon déduit de la colonne), à la hauteur de la ligne. -->\r\n <alg-field class=\"grid-editor\"\r\n [field]=\"getEditField(column)\"\r\n [form]=\"getRowForm(row)\"\r\n [showLabel]=\"false\"\r\n (propertyChanged)=\"onCellChanged(row, $event)\"\r\n >\r\n <!-- [focusable]=\"cellFocus\" -->\r\n </alg-field>\r\n </ng-template>\r\n <ng-template alg-viewMode>\r\n\r\n <div [ngSwitch]=\"column.Display?.type\" *ngIf=\"!isUndefined(row[column.Property])\">\r\n <div *ngSwitchCase=\"'url'\">\r\n <a [routerLink]=\"[$safeNavigationMigration(column.Display?.baseUrl),row[column.Property]]\">\r\n {{ row.Id || row[column.Property] }}\r\n </a>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'img'\" class=\"image\">\r\n <img *ngIf=\"column.Display?.imagePath?.length == 0\"\r\n [src]=\"(column.Display?.imgBase || '') + (column.Display?.sourceProperty ? row[column.Display?.sourceProperty] : row[column.Property])\" />\r\n\r\n <alg-image *ngIf=\"column.Display?.imagePath?.length != 0\"\r\n [imageUrl]=\"this.getEnumImageUrl(row,column)\" [imageHeight]=\"20\" [imageWidth]=\"20\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'img-file'\" class=\"image\">\r\n\r\n <alg-image\r\n [imageUrl]=\"this.getImageUrl(row,column)\" [wide]=\"true\" [imageHeight]=\"40\" [imageWidth]=\"40\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'wide-img'\" class=\"image\">\r\n <img *ngIf=\"column.Display?.imagePath?.length == 0\"\r\n [src]=\"(column.Display?.imgBase || '') + (column.Display?.sourceProperty ? row[column.Display?.sourceProperty] : row[column.Property])\" />\r\n\r\n <alg-image *ngIf=\"column.Display?.imagePath?.length != 0\" [wide]=\"true\"\r\n [imageUrl]=\"this.getEnumImageUrl(row,column)\" [imageHeight]=\"40\" [imageWidth]=\"40\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <span *ngSwitchCase=\"'date'\">\r\n {{row[column.Property]| date: column.Display?.format || 'dd/MM/yyyy'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'boolean'\">{{row[column.Property] ? 'OUI' : 'NON'}} </span>\r\n\r\n <span *ngSwitchCase=\"'datetime'\">{{row[column.Property] | date: column.Display?.format || 'dd/MM/yyyy HH:mm:ss'}}</span>\r\n\r\n <span *ngSwitchCase=\"'money'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\"\r\n class=\"money\">\r\n {{\r\n row[column.Property]\r\n | currency: column.Display?.currency\r\n || (column.Display?.sourceProperty && row[column.Display?.sourceProperty] ? row[column.Display?.sourceProperty] : false)\r\n || 'EUR' : 'symbol'\r\n\r\n | todefine: $safeNavigationMigration(column.Display?.handleError)\r\n }}\r\n\r\n <div class=\"tool-tip\" *ngIf=\"ctx.showOnToolTip(row,column)\">\r\n {{ctx.showOnToolTip(row,column)}}\r\n </div>\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'percent'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] * $safeNavigationMigration(column.Display?.multiplier) | percent : column.Display?.format\r\n || '1.0-2' | todefine: $safeNavigationMigration(column.Display?.handleError)}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'number'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] * $safeNavigationMigration(column.Display?.multiplier) | number: column.Display?.format\r\n || '1.0-3' | todefine : $safeNavigationMigration(column.Display?.handleError)}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'fixedsizeinteger'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] | tofixedsizeinteger : $safeNavigationMigration(column.Display?.completingCaracter) : $safeNavigationMigration(column.Display?.width) }}\r\n </span>\r\n <span *ngSwitchDefault [innerText]=\"column.Property == 'Id' ? (row[column.Property] < 0 ? '' : row[column.Property]) : row[column.Property]\">\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"isUndefined(row[column.Property])\">\r\n <span *ngIf=\"!isUndefined($safeNavigationMigration(column.Display?.nullCaseText))\" class=\"mw-invalid\">\r\n {{ column.Display?.nullCaseText }}\r\n </span>\r\n\r\n <span *ngIf=\"isUndefined($safeNavigationMigration(column.Display?.nullCaseText))\">\r\n &nbsp;&nbsp;&nbsp;\r\n </span>\r\n </div>\r\n\r\n </ng-template>\r\n\r\n </alg-datagrid-cell>\r\n </td>\r\n <ng-container *ngIf=\"ctx.enableLastColumnResize\">\r\n <!-- Colonne vide de fin : clic droit = menu de la dernière colonne. -->\r\n <td (contextmenu)=\"openContextMenu($event, row, rcolumns[rcolumns.length - 1])\"></td>\r\n </ng-container>\r\n </tr>\r\n </ng-container>\r\n </ng-container>\r\n </tbody>\r\n\r\n <tfoot>\r\n <tr class=\"empty-row\" *ngIf=\"hasAggregates\">\r\n <td></td>\r\n </tr>\r\n <tr class=\"aggregation-row\" *ngIf=\"hasAggregates\">\r\n <td class=\"row-state\"></td>\r\n <td *ngFor=\"let column of rcolumns; index as i\" [attr.class]=\"'cell' | gridStyle : ctx : { column : column, index : i }\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\">\r\n\r\n <ng-container *ngFor=\"let agg of columnAggregates[column.Property]\">\r\n\r\n <span class=\"agg-label\">{{ aggregationLabel(agg.op) }}</span>\r\n <span [ngSwitch]=\"agg.op === 'COUNT' ? 'count' : column.Display?.type\">\r\n\r\n <span *ngSwitchCase=\"'count'\">{{ agg.value | number: '1.0-0' }}</span>\r\n <span *ngSwitchCase=\"'percent'\">{{ agg.value | number: column.Display?.format || '1.0-3' }}</span>\r\n <span *ngSwitchCase=\"'fixedsizeinteger'\">{{ agg.value | number: column.Display?.format || '1.0-3' }}</span>\r\n\r\n <span *ngSwitchCase=\"'money'\" class=\"money\">\r\n {{ agg.value | currency: getColCurrency(column) }}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'date'\">\r\n {{agg.value | date: column.Display?.format || 'dd/MM/yyyy'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'datetime'\">\r\n {{agg.value | date: column.Display?.format || 'dd/MM/yyyy HH:mm:ss'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'number'\">\r\n {{agg.value | number: column.Display?.format || '1.0-3'}}\r\n </span>\r\n\r\n <span *ngSwitchDefault>\r\n {{agg.value}}\r\n </span>\r\n\r\n </span>\r\n\r\n </ng-container>\r\n\r\n </td>\r\n\r\n <ng-container *ngIf=\"ctx.enableLastColumnResize\">\r\n <td class=\"last-column\"></td>\r\n </ng-container>\r\n\r\n </tr>\r\n </tfoot>\r\n </ng-container>\r\n </ng-container>\r\n </table>\r\n </ng-container>\r\n</div>\r\n\r\n\r\n<!-- Emplacement visé par une colonne déplacée : trait orange sur la séparation des deux en-têtes. -->\r\n<div class=\"col-drop-indicator\" *ngIf=\"dropIndicator\"\r\n [ngStyle]=\"{ left: dropIndicator.left + 'px', top: dropIndicator.top + 'px', height: dropIndicator.height + 'px' }\"></div>\r\n\r\n<!-- Menu d'une colonne (flèche de l'en-tête ou clic droit sur une cellule), en trois zones séparées\r\n par un trait : Tri, Actions (celles du panneau des actions), Fonctions (sous-menu « Fonctions »). -->\r\n<div class=\"header-menu\" *ngIf=\"headerMenu\"\r\n [ngStyle]=\"{ top: headerMenu.y + 'px', left: headerMenu.x + 'px', visibility: headerMenu.ready ? 'visible' : 'hidden' }\"\r\n (click)=\"$event.stopPropagation()\" (contextmenu)=\"$event.preventDefault()\">\r\n <ul>\r\n <!-- Tri -->\r\n <li [class.checked]=\"this.facade.filter.isAsc(headerMenu.column.Property)\" (click)=\"sortColumn('asc')\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort-amount-asc\"></i><span>Trier par ordre croissant</span>\r\n </li>\r\n <li [class.checked]=\"this.facade.filter.isDesc(headerMenu.column.Property)\" (click)=\"sortColumn('desc')\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort-amount-desc\"></i><span>Trier par ordre décroissant</span>\r\n </li>\r\n <li [class.checked]=\"this.facade.filter.getSortList().length > 1\" (click)=\"openCustomSort()\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort\"></i><span>Tri personnalisé…</span>\r\n </li>\r\n <!-- Filtre sur la valeur de la cellule (clic droit sur une cellule) : première ligne des filtres composés. -->\r\n <li *ngIf=\"cellFilterValue() !== undefined\" (click)=\"filterOnCell()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-filter\"></i><span>Filtrer sur « {{ cellFilterLabel() }} »</span>\r\n </li>\r\n <!-- Affichage : vues personnalisées (cochée = affichée), puis « Modifier… ». -->\r\n <li *ngIf=\"this.facade.config.viewChooser\" class=\"has-submenu\" [class.open]=\"headerMenu.submenu === 'columns'\"\r\n (mouseenter)=\"setSubmenu('columns')\" (click)=\"setSubmenu('columns')\">\r\n <i class=\"fa fa-columns\"></i><span>Affichage</span><i class=\"fa fa-caret-right submenu-caret\"></i>\r\n\r\n <ul class=\"header-submenu header-submenu-columns\" *ngIf=\"headerMenu.submenu === 'columns'\"\r\n [class.left]=\"headerMenu.submenuLeft\" [class.up]=\"headerMenu.submenuUp\"\r\n (click)=\"$event.stopPropagation()\">\r\n <alg-grid-view-menu [chooser]=\"this.facade.config.viewChooser\" (done)=\"closeViewMenu()\" (loaded)=\"onViewMenuLoaded()\"></alg-grid-view-menu>\r\n </ul>\r\n </li>\r\n <!-- Colonnes figées à gauche, de la première jusqu'à celle-ci. -->\r\n <li [class.checked]=\"isFrozen(columnIndex(headerMenu.column))\" (click)=\"toggleFrozen()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-thumb-tack\"></i>\r\n <span>{{ frozenUntil === columnIndex(headerMenu.column) ? 'Libérer les colonnes' : 'Figer jusqu\\'à cette colonne' }}</span>\r\n </li>\r\n <li *ngIf=\"frozenUntil !== undefined && frozenUntil !== columnIndex(headerMenu.column)\"\r\n (click)=\"unfreezeColumns()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-unlock\"></i><span>Libérer les colonnes</span>\r\n </li>\r\n\r\n <!-- Actions -->\r\n <ng-container *ngIf=\"visibleActions.length > 0\">\r\n <li class=\"separator\"></li>\r\n <ng-container *ngFor=\"let action of visibleActions\">\r\n <ng-container *ngIf=\"{ disabled: (action.disabled$ | async) } as state\">\r\n <li class=\"menu-action\" [class.disabled]=\"state.disabled\"\r\n (click)=\"executeMenuAction(action, state.disabled)\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <img class=\"menu-action-icon\" [src]=\"getActionImageUrl(action.icon)\" [alt]=\"action.title\" />\r\n <span>{{ action.title }}</span>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- Affichage -->\r\n <li class=\"separator\"></li>\r\n <li class=\"has-submenu\" [class.open]=\"headerMenu.submenu === 'display'\"\r\n (mouseenter)=\"setSubmenu('display')\" (click)=\"setSubmenu('display')\">\r\n <i class=\"fa fa-calculator\"></i><span>Fonctions</span><i class=\"fa fa-caret-right submenu-caret\"></i>\r\n\r\n <ul class=\"header-submenu\" *ngIf=\"headerMenu.submenu === 'display'\"\r\n [class.left]=\"headerMenu.submenuLeft\" [class.up]=\"headerMenu.submenuUp\"\r\n (click)=\"$event.stopPropagation()\">\r\n <li *ngFor=\"let op of getAggregationOptions(headerMenu.column)\"\r\n [class.checked]=\"activeAggregations[headerMenu.column.Property] === op\"\r\n (click)=\"aggregateColumn(op)\">\r\n <i class=\"fa fa-check\"></i><span>{{ aggregationLabel(op) }}</span>\r\n </li>\r\n <ng-container *ngIf=\"activeAggregations[headerMenu.column.Property]\">\r\n <li class=\"separator\"></li>\r\n <li (click)=\"aggregateColumn(null)\">\r\n <i class=\"fa fa-times\"></i><span>Ne plus afficher</span>\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ul>\r\n</div>\r\n\r\n",
18790
18914
  styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{flex:1}.header.last-column{border-left:none!important;width:100%}table thead .sorting,table thead .sorting_asc,table thead .sorting_desc,table thead .sorting_asc_disabled,table thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table thead th:first-child,table tfoot tr:first-child{position:sticky;left:0;z-index:2}table tbody th:first-child{position:sticky;left:0;z-index:1;border-right:none}table tbody th:first-child div{text-align:center;vertical-align:middle;line-height:20px}table tbody tr.row-odd th:first-child{background:#ebf0ff}table tbody tr.selected.row-odd.mw-invalid th:first-child,table tbody tr.selected.row-even.mw-invalid th:first-child,table tbody tr.selected.row-odd.mw-warning th:first-child,table tbody tr.selected.row-even.mw-warning th:first-child,table tbody tr.selected.row-even th:first-child,table tbody tr.selected.row-odd th:first-child{background-color:#8ea7f8;color:#fff}table tbody tr.row-odd.mw-invalid th:first-child,table tbody tr.row-even.mw-invalid th:first-child{background-color:#f2dede}table tbody tr.row-odd.mw-warning th:first-child,table tbody tr.row-even.mw-warning th:first-child{background-color:#fff4de}table thead th{position:-webkit-sticky;position:sticky;top:0;z-index:1;background:#fff;border-left:1px solid #3C5DC4;border-bottom:1px solid #3C5DC4}table thead .sorting{background-image:none;cursor:pointer}table thead .sorting_asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMTgwMTE3NDA3MjA2ODExQjM4MkY2QzVGRUYwRTJDNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4MkFEQzYxNjIyQzExMUUxQTFGMUFEQUQ1QjJBNTM4QyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4MkFEQzYxNTIyQzExMUUxQTFGMUFEQUQ1QjJBNTM4QyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTFCMzgyRjZDNUZFRjBFMkM0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjAxODAxMTc0MDcyMDY4MTFCMzgyRjZDNUZFRjBFMkM0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+z5ABTAAAAI5JREFUeNpi/P//PwO1ABMDFQELIQXVjfe4gFQbEJe11iv9otRl2UCcBcSphBQy4gszoKu0gNROIJYB4jtA7AF03V2SXQY0iBFIFUMNAgEVIM6DipPsTQcgDkQTSwRia5IMA9rOC6RqgVgQTQokXgOUZyfFZSFQF/zAgh2BOIjkCBjQRDtq2Khh9DAMIMAAT9AmNBDSXegAAAAASUVORK5CYII=);cursor:pointer}table thead .sorting_desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMTgwMTE3NDA3MjA2ODExQjM4MkY2QzVGRUYwRTJDNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4MkFEQzYxQTIyQzExMUUxQTFGMUFEQUQ1QjJBNTM4QyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4MkFEQzYxOTIyQzExMUUxQTFGMUFEQUQ1QjJBNTM4QyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTFCMzgyRjZDNUZFRjBFMkM0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjAxODAxMTc0MDcyMDY4MTFCMzgyRjZDNUZFRjBFMkM0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+1fsfwAAAAJdJREFUeNpi/P//PwO1ABMDFcGoYaOG0cMwFmyC1Y33IoHUAiD+h8MBGa31SvOJddk6IN4PxBxY8FEgXkO0N4G2/gRSLUD8GU3qPRA3A+U/kxpmIBege2U9EB/ApYERX6kBDDtlILUDiFWA+AkQuwNddY2s2ARqvAukJgDxbyCehM8gnLGJBmYDsSoQTyWkkHHQFo4AAQYAAA0piq4hbqwAAAAASUVORK5CYII=);cursor:pointer}table thead .sorting_asc_disabled,table thead .sorting_desc_disabled{background-image:none}.checkbok-container{align-items:center;margin:0;padding:0}.gridcell-container{padding:0;min-width:fit-content}.gridcell-container input[type=number]{display:block;size:1;font-size:10pt;width:100%!important;height:100%;margin:0!important;border:2px solid #F78383}.datagrid-custom-container{height:100%;overflow:auto;margin-left:-1px}.data-grid-datatable{table-layout:fixed;cursor:pointer;border-spacing:0px}.data-grid-datatable td{box-shadow:none;padding:2px 5px;white-space:nowrap}.data-grid-datatable td.row-state{padding:2px 5px;width:16px}.data-grid-datatable td.img{padding:2px 5px}.data-grid-datatable td.row-state img{height:16px;width:16px;margin-top:-3px;padding:0}.data-grid-datatable tr{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.header{vertical-align:bottom;padding:2px 5px 2px 0;font-weight:400;color:#3c5dc4;white-space:nowrap;line-height:25px;min-width:32px;margin-bottom:5px}.header.img{min-width:16px}.header.row-state{min-width:27px;position:-webkit-sticky;position:sticky;top:0;left:0;z-index:2;background:#fff;border-left:none;border-right:none}.filter-header{background-color:#f7f7f7!important}.filter-header.disabled{background-color:#9fa9c7!important}.filter-header.row-state:hover:before{position:relative;display:block;float:left;width:16px;height:16px;content:\" \";background-size:16px 16px;-webkit-background-size:16px 16px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"57\" height=\"57\" viewBox=\"0 0 57 57\">%0D%0A <path id=\"Rond_Croix\" data-name=\"Rond Croix\" d=\"M28.5,57A28.5,28.5,0,0,1,8.347,8.347,28.5,28.5,0,1,1,48.652,48.652,28.317,28.317,0,0,1,28.5,57Zm0-24.036h0l11.9,11.9L44.869,40.4,32.964,28.5,44.869,16.593,40.4,12.129,28.5,24.034,16.594,12.129l-4.465,4.464L24.034,28.5,12.129,40.4l4.463,4.465L28.5,32.965Z\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 0 0 3px;filter:brightness(0) saturate(100%) invert(70%) sepia(93%) saturate(1540%) hue-rotate(309deg) brightness(105%) contrast(94%)}.filter-header-input{height:calc(100% - 2px);width:calc(100% - 2px);margin:0;border:none!important;background-color:#f7f7f7;padding:0 0 0 5px;color:#4d5a81}.header.row-state:before{position:relative;display:block;float:left;width:16px;height:16px;content:\" \";background-size:16px 16px;-webkit-background-size:16px 16px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->%0D%0A<path fill=\"currentColor\" d=\"M3.9 54.9C10.5 40.9 24.5 32 40 32l432 0c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9 320 448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6l0-79.1L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z\"/></svg>');background-repeat:no-repeat;margin:0 0 3px 5px;filter:brightness(0) saturate(100%) invert(56%) sepia(79%) saturate(2301%) hue-rotate(202deg) brightness(92%) contrast(91%)}.filter-header.last-column{border-left:none!important}.header label{cursor:pointer;margin-bottom:0;margin-right:10px;padding-left:5px;width:100%}.row-odd{background-color:#ebf0ff;border-left:1px solid #C1D0FD;border-bottom:1px solid #C1D0FD}.row-even{background-color:#f7faff;border-left:1px solid #C1D0FD;border-bottom:1px solid #C1D0FD}.error-row-icon{padding:2px;width:20px}.selected{background-color:#8ea7f8;color:#fff}.mw-invalid{font-weight:600;background-color:#f2dede;border:1px solid var(--red-error)}.mw-invalid td{background-color:#f2dede!important}.data-grid-datatable tbody tr.mw-invalid>td,.data-grid-datatable tbody tr.mw-invalid>th{box-shadow:inset 0 1px 0 var(--red-error),inset 0 -1px 0 var(--red-error)}span.mw-invalid{padding:0 3px;color:var(--red-error);background-color:transparent}.mw-warning.selected,.mw-invalid.selected{background-color:#8ea7f8}.mw-warning.selected td,.mw-invalid.selected td{background-color:#8ea7f8!important}.mw-warning{font-weight:600;background-color:#fff4de;border:1px solid var(--orange-algorima)}.mw-warning td{background-color:#fff4de!important}.mw-modified{font-weight:600;border:1px solid var(--main-bg-color)}.first-row{border-top:none}.last-row,.last-row td{border-bottom:none!important}.data-grid-datatable>tbody>tr{z-index:50}.image{position:relative;display:flex;flex-direction:row}.image img{height:20px;width:auto}td.money{position:relative}.tool-tip{visibility:hidden;background-color:#424242;color:#fff;text-align:left;border-radius:3px;font-size:15px;position:absolute;z-index:1;top:-10%;left:150%;opacity:0;transition:opacity .3s;padding:5px 15px;white-space:nowrap;overflow:visible}.tool-tip:after{content:\" \";position:absolute;top:50%;right:100%;margin-top:-5px;border-width:5px;border-style:solid;border-color:transparent rgb(66,66,66) transparent transparent}.image:hover .tool-tip{visibility:visible;opacity:1}td.money:hover .tool-tip{visibility:visible;opacity:1;left:100%;height:28px;font-size:13px;margin-top:0;padding:5px 10px 10px}.disabled{pointer-events:none}.edit-mode{background-color:#fff;border:1px solid #4465C9;min-width:90px!important;height:30px!important}.edit-mode .form-group{min-height:0px}.edit-mode .inactivated{background-color:#dbe4ff80}.edit-mode .inactivated-fo{background-color:#dbe4ff}.edit-mode .ecartclass{border:2px solid #4465C9}.edit-mode .error-cell{font-weight:700;color:#f78383!important}.edit-mode .selected{color:#4d5a81}.highlight-1,.highlight-1.selected,.edit-mode.highlight-1.selected{background-color:#4d5a81!important;color:#fff!important}.highlight-2,.highlight-2.selected,.edit-mode.highlight-2.selected{background-color:#dbe4ff!important;color:#4d5a81!important}.highlight-3,.highlight-3.selected,.edit-mode.highlight-3.selected{background-color:#4465c9!important;color:#fff!important}.highlight-4,.highlight-4.selected,.edit-mode.highlight-4.selected{background-color:#f78383!important;color:#fff!important}.highlight-5,.highlight-5.selected{background-color:#4465c9!important;color:#fff}.fixed-column{position:-webkit-sticky;position:sticky;left:27px;z-index:2!important;border-left:none}.fixed-header{position:-webkit-sticky;position:sticky;left:27px;z-index:3!important;background:#fff}.has-focus{padding:0!important}.has-focus ::ng-deep input,.has-focus ::ng-deep input:focus-visible,.has-focus ::ng-deep input:focus{border-radius:.1;outline:none!important;width:100%;height:29px;min-height:0;margin:0;border:1px solid #4465C9}.has-focus ::ng-deep .form-group{margin-bottom:0;min-height:0}.has-focus ::ng-deep .field-container{margin-bottom:0}.edit-mode.has-focus{width:90px!important}.bordered{border-left:1px solid #14358f!important;border-right:1px solid #14358f!important}.match-keywords{color:var(--main-bg-color);font-weight:700}.datagrid-img img{height:20px;width:20px}.right-align{text-align:right}.left-align{text-align:left}.detail-div{height:0px;overflow:hidden;transition:all .2s linear;width:100%}.fa-custom{font-size:smaller}.detail-div-show{height:150px;transition:all .2s linear}.toggleable{visibility:collapse}.toggleable-show{visibility:visible}.toggle-button-down{transform:rotate(90deg);transition:all .2s linear}.no-result{color:#5272d3;height:100%;width:100%;padding:15px 15px 150px}.new-rowstate{display:flex;flex-direction:row;align-items:center;align-self:center;height:100%;width:100%}.new-rowstate span{height:100%;width:100%;margin-top:-6px}.context-menu{position:absolute;background:#ffffff26;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.2);box-shadow:0 4px 12px #0000004d;padding:8px;z-index:1000;min-width:75px;max-width:250px;transition:all .3s ease-in-out}.context-menu ul{list-style:none;margin:0;padding:0}.context-menu li{padding:10px 14px;cursor:pointer;border-radius:8px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}.context-menu li:hover{background:linear-gradient(135deg,#ffffff4d,#ffffff1a);transform:scale(1.02)}.context-menu li:active{transform:scale(.98)}.grid-container{overflow:auto;position:relative}.aggregation-row{position:sticky;bottom:0;z-index:50;background:#f7faff!important;background-color:#f7faff!important;font-weight:700;border-top:1px solid #4D5A81!important;border-bottom:1px solid #4D5A81!important}.aggregation-row td{background:#f7faff!important;background-color:#f7faff!important;border-top:1px solid #4D5A81!important;border-bottom:1px solid #4D5A81!important;border-left:none!important;border-right:none!important}.empty-row td{padding:0}.col-resizer{position:absolute;top:0;right:-5px;z-index:4;width:10px;height:100%;cursor:col-resize}.col-resizer:hover{background:#3c5dc440}.data-grid-datatable td[data-sized]{overflow:hidden;text-overflow:ellipsis}:where(.data-grid-datatable tbody tr,.data-grid-datatable tfoot tr){background-color:#fff}.data-grid-datatable td[data-frozen]{position:sticky;z-index:1;background-color:inherit}.data-grid-datatable thead th[data-frozen]{position:sticky;z-index:3}.data-grid-datatable td[data-frozen]:has(+td:not([data-frozen])),.data-grid-datatable thead th[data-frozen]:has(+th:not([data-frozen])){box-shadow:inset -2px 0 #3c5dc459}table thead th.header{background-image:none!important;cursor:default}.header-cell{position:relative;display:flex;align-items:center;gap:2px}.header-cell label{flex:1 1 auto;cursor:default;margin:0;padding-left:2px;overflow:hidden;text-overflow:ellipsis}.header-cell.col-dragging{opacity:.8}.dg-column-ghost{border:1px solid #C1D0FD;box-sizing:border-box}.col-drop-indicator{position:fixed;z-index:3100;width:1px;border-radius:0;background:var(--orange-algorima, #f59d0f);pointer-events:none}.header-cell-end{justify-content:flex-end;align-items:center;height:25px}.header-handle{flex-shrink:0;display:flex;align-items:center;justify-content:center;box-sizing:content-box;width:17px;height:20px;padding:0 2px;color:#3c5dc4;opacity:.35;cursor:grab}.header-handle:hover{opacity:1}.header-handle:active{cursor:grabbing}.header-sort,.header-agg{flex-shrink:0;color:#3c5dc4;font-size:11px;font-style:normal}.header-menu-button{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:16px;height:18px;padding:0;border:0;border-radius:3px;background:none;color:#3c5dc4;opacity:.5;cursor:pointer}.header-menu-button:hover,.header-menu-button.open{opacity:1;background:#4465c926}.header-menu{position:fixed;z-index:3000;width:230px;padding:4px 0;background:#fff;border:1px solid #DBE4FF;border-radius:5px;box-shadow:0 4px 14px #0003}.header-menu ul{margin:0;padding:0;list-style:none}.header-menu li{display:flex;align-items:center;gap:8px;padding:5px 12px;color:#3c5dc4;font-size:12.5px;cursor:pointer}.header-menu li i{width:14px;text-align:center;visibility:visible}.header-menu li:hover{background:#dbe4ff}.header-menu li.checked{font-weight:600}.header-menu li.separator{height:1px;margin:4px 0;padding:0;background:#dbe4ff;cursor:default}.header-menu li .fa-check{visibility:hidden}.header-menu li.checked>.fa-check{visibility:visible}.header-menu li.menu-action.disabled{opacity:.45;cursor:default}.header-menu li.menu-action.disabled:hover{background:none}.header-menu .menu-action-icon{width:14px;height:14px}.header-menu li.has-submenu{position:relative}.header-menu li.has-submenu.open{background:#dbe4ff}.header-menu li.has-submenu .submenu-caret{margin-left:auto}.header-menu .header-submenu{position:absolute;top:-5px;left:100%;width:200px;padding:4px 0;background:#fff;border:1px solid #DBE4FF;border-radius:5px;box-shadow:0 4px 14px #0003;font-weight:400}.header-menu .header-submenu.left{left:auto;right:100%}.header-menu .header-submenu.header-submenu-columns{width:240px;max-height:320px;overflow-y:auto}.header-menu .header-submenu .column-choice input[type=checkbox]{width:14px;margin:0;pointer-events:none}.header-menu .header-submenu .submenu-loading{opacity:.6;cursor:default}.header-menu .header-submenu .view-lock{margin-left:auto;padding-left:8px;opacity:.5;font-size:11px}.header-menu .header-submenu.up{top:auto;bottom:-5px}.header-sort-rank{flex-shrink:0;color:#3c5dc4;font-size:9px}.aggregation-row .agg-label{margin-right:4px;color:#3c5dc4;font-size:10.5px;opacity:.7}.datagrid-custom-container:focus,.datagrid-custom-container:focus-visible{outline:none}.data-grid-datatable td .image ::ng-deep img{display:block}.data-grid-datatable td:has(>.grid-cursor){outline:2px solid #4465C9;outline-offset:-2px}.data-grid-datatable td:has(>.grid-range){background-color:#4465c940!important;box-shadow:inset 0 0 0 1px #4465c980;color:#4d5a81!important}.data-grid-datatable td:has(>.grid-range) ::ng-deep *{color:#4d5a81!important}.data-grid-datatable td:has(.grid-editor){padding:0!important;background-color:#fff}.grid-editor{display:block;height:23px;overflow:visible;color:#4d5a81}.grid-editor ::ng-deep .field-container,.grid-editor ::ng-deep .form-group,.grid-editor ::ng-deep .form-group>div,.grid-editor ::ng-deep .checkbox-container{margin:0!important;padding:0;min-height:0!important;height:23px}.grid-editor ::ng-deep .validation-message,.grid-editor ::ng-deep .checkbox-label{display:none}.grid-editor ::ng-deep input,.grid-editor ::ng-deep input:focus,.grid-editor ::ng-deep textarea{box-sizing:border-box;width:100%;height:23px!important;min-height:0!important;margin:0!important;padding:0 4px;border:1px solid #4465C9;border-radius:0;box-shadow:none;font-size:inherit;line-height:normal;resize:none}.grid-editor ::ng-deep input.ng-invalid.ng-touched,.grid-editor ::ng-deep input.ng-invalid.ng-dirty{border-color:#f78383}.grid-editor ::ng-deep input[type=checkbox]{width:auto}.grid-editor ::ng-deep .ng-select .ng-select-container{color:#4d5a81;min-height:0!important;height:23px!important;border-radius:0;border-color:#4465c9}.grid-editor ::ng-deep .ng-select.ng-select-single .ng-select-container .ng-value-container .ng-input{top:0}.grid-editor ::ng-deep .ng-select .ng-select-container .ng-value-container{padding-top:0}.grid-editor ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-input>input{height:100%!important;border:none}.grid-editor ::ng-deep .form-group>div{flex:1;min-width:0;width:100%}.grid-editor ::ng-deep .ng-select{width:100%}.grid-editor ::ng-deep alg-selector{display:flex;width:100%;height:23px}.grid-editor ::ng-deep .input-select-default,.grid-editor ::ng-deep .input-select-default.with-button{flex:1;min-width:0;padding-right:0;margin-right:0}.grid-editor ::ng-deep .input-group-btn{float:none}.grid-editor ::ng-deep .btn,.grid-editor ::ng-deep .btn-32,.grid-editor ::ng-deep .btn-input-select{height:23px;width:23px;padding:0;line-height:1;border-radius:0}.grid-editor ::ng-deep .input-with-button{display:flex;height:23px}.grid-editor ::ng-deep .switch{margin:2px 0 0 4px;transform:scale(.7);transform-origin:left top}::ng-deep body.dg-column-dragging,::ng-deep body.dg-column-dragging *{cursor:grabbing!important;-webkit-user-select:none!important;user-select:none!important}\n"],
18791
18915
  dependencies: [
18792
18916
  {
@@ -19006,7 +19130,7 @@ i0.ɵɵngDeclareClassMetadata({
19006
19130
  provide: RowRefHost,
19007
19131
  useExisting: forwardRef(() => DataGridComponent)
19008
19132
  }],
19009
- template: "<div #datagridContainer [class]=\"this.datagridClass\" tabindex=\"-1\" (keydown)=\"onGridKeyDown($event)\"\r\n (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\" (contextmenu)=\"openEmptyAreaContextMenu($event)\">\r\n <ng-container>\r\n <table class=\"data-grid-datatable\">\r\n <ng-container *ngIf=\"this.facade.style as ctx\">\r\n <ng-container *ngIf=\"this.facade.config.columns$ | async as rcolumns\">\r\n <thead>\r\n <tr>\r\n <th class=\"header row-state\" (click)=\"addFilterQuery()\"></th>\r\n <!-- En-tête : poignée de déplacement à gauche, intitulé, indicateur de tri,\r\n flèche du menu (tri, agrégation) à droite. Le clic ne trie plus. -->\r\n <th #thHeader *ngFor=\"let col of rcolumns;let i = index\"\r\n [attr.id]=\"guid.toString() + '-col-' + col.Property\"\r\n [attr.class]=\"'header' | gridStyle : ctx : { column : col }\"\r\n [style.width.px]=\"colWidth(col)\" [style.min-width.px]=\"colWidth(col)\" [style.max-width.px]=\"colWidth(col)\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\">\r\n <div class=\"header-cell\"\r\n [class.col-dragging]=\"draggedColumn === col.Property\">\r\n <span class=\"header-handle\" title=\"Déplacer la colonne\"\r\n (mousedown)=\"onHeaderMouseDown(col, $event)\">\r\n <i class=\"fa fa-ellipsis-v\"></i>\r\n </span>\r\n <label>{{ getHeaderLabel(col) }}</label>\r\n <i *ngIf=\"this.facade.filter.isAsc(col.Property)\" class=\"fa fa-long-arrow-up header-sort\" title=\"Tri croissant\"></i>\r\n <i *ngIf=\"this.facade.filter.isDesc(col.Property)\" class=\"fa fa-long-arrow-down header-sort\" title=\"Tri décroissant\"></i>\r\n <sup *ngIf=\"this.facade.filter.sortRank(col.Property) > 0\" class=\"header-sort-rank\"\r\n title=\"Rang dans le tri personnalisé\">{{ this.facade.filter.sortRank(col.Property) }}</sup>\r\n <i *ngIf=\"activeAggregations[col.Property]\" class=\"header-agg\" [title]=\"'Agrégation : ' + aggregationLabel(activeAggregations[col.Property])\">Σ</i>\r\n <!-- Dernière colonne suivie de la colonne vide de fin : sa flèche est dans la colonne vide. -->\r\n <button type=\"button\" class=\"header-menu-button\" title=\"Trier, agréger\"\r\n *ngIf=\"!(ctx.enableLastColumnResize && i === rcolumns.length - 1)\"\r\n [class.open]=\"headerMenu?.column.Property === col.Property\"\r\n (click)=\"toggleHeaderMenu(col, $event)\">\r\n <i class=\"fa fa-caret-down\"></i>\r\n </button>\r\n </div>\r\n <!-- Bord droit : largeur de la colonne (tirer), ajustée au contenu (double clic). -->\r\n <span class=\"col-resizer\" title=\"Largeur de la colonne (double clic : ajuster au contenu)\"\r\n (mousedown)=\"startResize(col, $event)\" (dblclick)=\"autoFitColumn(col, $event)\"\r\n (click)=\"$event.stopPropagation()\"></span>\r\n </th>\r\n <th *ngIf=\"ctx.enableLastColumnResize\" #lastColumnHeader class=\"header last-column\"\r\n (contextmenu)=\"openHeaderContextMenu($event, rcolumns[rcolumns.length - 1])\">\r\n <div class=\"header-cell header-cell-end\" *ngIf=\"rcolumns.length > 0\">\r\n <button type=\"button\" class=\"header-menu-button\" title=\"Trier, agréger\"\r\n [class.open]=\"headerMenu?.column.Property === rcolumns[rcolumns.length - 1].Property\"\r\n (click)=\"toggleHeaderMenu(rcolumns[rcolumns.length - 1], $event)\">\r\n <i class=\"fa fa-caret-down\"></i>\r\n </button>\r\n </div>\r\n </th>\r\n\r\n </tr>\r\n <ng-container *ngIf=\"this.facade.filter.additionalQuery$ | async as additionalQuery\">\r\n <tr #thFilterRows *ngFor=\"let filterRow of additionalQuery.Rows;let i = index\" >\r\n <th class=\"filter-header row-state\" (click)=\"removeFilterQuery(i)\"></th>\r\n <th class=\"filter-header\" [ngClass]=\"{disabled: !col.IsFilterEnabled}\" *ngFor=\"let col of rcolumns;let i = index\"\r\n [attr.id]=\"guid.toString() + '-filter-' + i\">\r\n <input *ngIf=\"col.IsFilterEnabled\" type=\"text\"\r\n class=\"filter-header-input\"\r\n name=\"keyword\"\r\n #uniquename=\"ngModel\"\r\n [attr.id]=\"guid.toString() + '-filter-input-' + col.Property\"\r\n [(ngModel)]=\"filterRow.ClausesByPropertyName.get(col.Property).FilterValue\"\r\n (ngModelChange)=\"handleFilterChanged($event, filterRow, col.Property)\"\r\n (change)=\"validateFilterChanged($event, filterRow, col.Property)\"\r\n />\r\n </th>\r\n <ng-container>\r\n <th #lastColumnFilter class=\"filter-header last-column\"></th>\r\n </ng-container>\r\n </tr>\r\n </ng-container>\r\n </thead>\r\n\r\n <tbody>\r\n <ng-container *ngIf=\"this.facade.data\">\r\n <ng-container *ngFor=\"let row of this.data.Values; let r = index;\">\r\n <tr\r\n [class]=\"'alternating' | gridStyle : ctx : { row : row, index : r, maxIndex : this.data.Values.length, hasAggregates: hasAggregates }\"\r\n [class.selected]=\"isRowSelected(row)\"\r\n [attr.id]=\"guid.toString() + '-row-' + row.Id\"\r\n (mousedown)=\"onRowMouseDown(row, $event)\"\r\n (mouseup)=\"onRowMouseUp(row, $event)\"\r\n (click)=\"onRowClick(row, $event)\"\r\n\r\n [rowRef]=\"guid + '-row-' + row.Id\"\r\n >\r\n\r\n <th [attr.class]=\"'rowstate' | gridStyle : ctx : { row : row }\">\r\n <ng-container *ngIf=\"selectionStyler.isModified(row) && !selectionStyler.isInvalid(row) && !selectionStyler.isWarning(row)\">\r\n <div class=\"new-rowstate\"><span>...</span></div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectionStyler.isInvalid(row)\">\r\n <div> <img class=\"error-row-icon\" src=\"assets/images/error.svg\" /> </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectionStyler.isWarning(row) && !selectionStyler.isInvalid(row)\">\r\n <div> <img class=\"error-row-icon\" src=\"assets/images/warning.svg\" /> </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"ctx.displayDetail\">\r\n <button type=\"button\" [attr.id]=\"row.Id + '_button'\" class=\"toggle-button btn\" (click)=\"toggleDetail(row)\">\r\n <i class=\"fa fa-chevron-right fa-custom\"></i>\r\n </button>\r\n </ng-container>\r\n </th>\r\n\r\n <td *ngFor=\"let column of rcolumns; index as i\"\r\n [attr.id]=\"cellId(row, column)\"\r\n [style.max-width.px]=\"colWidth(column)\" [attr.data-sized]=\"colWidth(column) ? '' : null\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\"\r\n [class]=\"'cell' | gridStyle : ctx : { row : row,column : column,index : r }\"\r\n [class.selected]=\"isRowSelected(row)\"\r\n (mousedown)=\"onCellMouseDown(r, i, $event)\"\r\n (mouseenter)=\"onCellMouseEnter(r, i, $event)\"\r\n (click)=\"onCellClick(row, column)\"\r\n (dblclick)=\"onCellDblClick(row, column)\">\r\n\r\n <alg-datagrid-cell #dataGridCellComponent\r\n class=\"gridcell-container\"\r\n [entity]=\"row\"\r\n [isEditable]=\"canEditCell(row, column)\"\r\n [property]=\"column.Property\"\r\n [managed]=\"true\"\r\n [mode]=\"showEditor(row, column) ? 'edit' : 'view'\"\r\n [class.grid-cursor]=\"isCursorCell(row, column)\"\r\n [class.grid-range]=\"isInRange(r, i)\"\r\n (contextmenu)=\"openContextMenu($event, row, column)\">\r\n\r\n <ng-template alg-editMode>\r\n <!-- Éditeur du champ du formulaire qui correspond à la colonne (sinon déduit de la colonne), à la hauteur de la ligne. -->\r\n <alg-field class=\"grid-editor\"\r\n [field]=\"getEditField(column)\"\r\n [form]=\"getRowForm(row)\"\r\n [showLabel]=\"false\"\r\n (propertyChanged)=\"onCellChanged(row, $event)\"\r\n >\r\n <!-- [focusable]=\"cellFocus\" -->\r\n </alg-field>\r\n </ng-template>\r\n <ng-template alg-viewMode>\r\n\r\n <div [ngSwitch]=\"column.Display?.type\" *ngIf=\"!isUndefined(row[column.Property])\">\r\n <div *ngSwitchCase=\"'url'\">\r\n <a [routerLink]=\"[$safeNavigationMigration(column.Display?.baseUrl),row[column.Property]]\">\r\n {{ row.Id || row[column.Property] }}\r\n </a>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'img'\" class=\"image\">\r\n <img *ngIf=\"column.Display?.imagePath?.length == 0\"\r\n [src]=\"(column.Display?.imgBase || '') + (column.Display?.sourceProperty ? row[column.Display?.sourceProperty] : row[column.Property])\" />\r\n\r\n <alg-image *ngIf=\"column.Display?.imagePath?.length != 0\"\r\n [imageUrl]=\"this.getEnumImageUrl(row,column)\" [imageHeight]=\"20\" [imageWidth]=\"20\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'img-file'\" class=\"image\">\r\n\r\n <alg-image\r\n [imageUrl]=\"this.getImageUrl(row,column)\" [wide]=\"true\" [imageHeight]=\"40\" [imageWidth]=\"40\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'wide-img'\" class=\"image\">\r\n <img *ngIf=\"column.Display?.imagePath?.length == 0\"\r\n [src]=\"(column.Display?.imgBase || '') + (column.Display?.sourceProperty ? row[column.Display?.sourceProperty] : row[column.Property])\" />\r\n\r\n <alg-image *ngIf=\"column.Display?.imagePath?.length != 0\" [wide]=\"true\"\r\n [imageUrl]=\"this.getEnumImageUrl(row,column)\" [imageHeight]=\"40\" [imageWidth]=\"40\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <span *ngSwitchCase=\"'date'\">\r\n {{row[column.Property]| date: column.Display?.format || 'dd/MM/yyyy'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'boolean'\">{{row[column.Property] ? 'OUI' : 'NON'}} </span>\r\n\r\n <span *ngSwitchCase=\"'datetime'\">{{row[column.Property] | date: column.Display?.format || 'dd/MM/yyyy HH:mm:ss'}}</span>\r\n\r\n <span *ngSwitchCase=\"'money'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\"\r\n class=\"money\">\r\n {{\r\n row[column.Property]\r\n | currency: column.Display?.currency\r\n || (column.Display?.sourceProperty && row[column.Display?.sourceProperty] ? row[column.Display?.sourceProperty] : false)\r\n || 'EUR' : 'symbol'\r\n\r\n | todefine: $safeNavigationMigration(column.Display?.handleError)\r\n }}\r\n\r\n <div class=\"tool-tip\" *ngIf=\"ctx.showOnToolTip(row,column)\">\r\n {{ctx.showOnToolTip(row,column)}}\r\n </div>\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'percent'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] * $safeNavigationMigration(column.Display?.multiplier) | percent : column.Display?.format\r\n || '1.0-2' | todefine: $safeNavigationMigration(column.Display?.handleError)}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'number'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] * $safeNavigationMigration(column.Display?.multiplier) | number: column.Display?.format\r\n || '1.0-3' | todefine : $safeNavigationMigration(column.Display?.handleError)}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'fixedsizeinteger'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] | tofixedsizeinteger : $safeNavigationMigration(column.Display?.completingCaracter) : $safeNavigationMigration(column.Display?.width) }}\r\n </span>\r\n <span *ngSwitchDefault [innerText]=\"column.Property == 'Id' ? (row[column.Property] < 0 ? '' : row[column.Property]) : row[column.Property]\">\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"isUndefined(row[column.Property])\">\r\n <span *ngIf=\"!isUndefined($safeNavigationMigration(column.Display?.nullCaseText))\" class=\"mw-invalid\">\r\n {{ column.Display?.nullCaseText }}\r\n </span>\r\n\r\n <span *ngIf=\"isUndefined($safeNavigationMigration(column.Display?.nullCaseText))\">\r\n &nbsp;&nbsp;&nbsp;\r\n </span>\r\n </div>\r\n\r\n </ng-template>\r\n\r\n </alg-datagrid-cell>\r\n </td>\r\n <ng-container *ngIf=\"ctx.enableLastColumnResize\">\r\n <!-- Colonne vide de fin : clic droit = menu de la dernière colonne. -->\r\n <td (contextmenu)=\"openContextMenu($event, row, rcolumns[rcolumns.length - 1])\"></td>\r\n </ng-container>\r\n </tr>\r\n </ng-container>\r\n </ng-container>\r\n </tbody>\r\n\r\n <tfoot>\r\n <tr class=\"empty-row\" *ngIf=\"hasAggregates\">\r\n <td></td>\r\n </tr>\r\n <tr class=\"aggregation-row\" *ngIf=\"hasAggregates\">\r\n <td class=\"row-state\"></td>\r\n <td *ngFor=\"let column of rcolumns; index as i\" [attr.class]=\"'cell' | gridStyle : ctx : { column : column, index : i }\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\">\r\n\r\n <ng-container *ngFor=\"let agg of columnAggregates[column.Property]\">\r\n\r\n <span class=\"agg-label\">{{ aggregationLabel(agg.op) }}</span>\r\n <span [ngSwitch]=\"agg.op === 'COUNT' ? 'count' : column.Display?.type\">\r\n\r\n <span *ngSwitchCase=\"'count'\">{{ agg.value | number: '1.0-0' }}</span>\r\n <span *ngSwitchCase=\"'percent'\">{{ agg.value | number: column.Display?.format || '1.0-3' }}</span>\r\n <span *ngSwitchCase=\"'fixedsizeinteger'\">{{ agg.value | number: column.Display?.format || '1.0-3' }}</span>\r\n\r\n <span *ngSwitchCase=\"'money'\" class=\"money\">\r\n {{ agg.value | currency: getColCurrency(column) }}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'date'\">\r\n {{agg.value | date: column.Display?.format || 'dd/MM/yyyy'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'datetime'\">\r\n {{agg.value | date: column.Display?.format || 'dd/MM/yyyy HH:mm:ss'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'number'\">\r\n {{agg.value | number: column.Display?.format || '1.0-3'}}\r\n </span>\r\n\r\n <span *ngSwitchDefault>\r\n {{agg.value}}\r\n </span>\r\n\r\n </span>\r\n\r\n </ng-container>\r\n\r\n </td>\r\n\r\n <ng-container *ngIf=\"ctx.enableLastColumnResize\">\r\n <td class=\"last-column\"></td>\r\n </ng-container>\r\n\r\n </tr>\r\n </tfoot>\r\n </ng-container>\r\n </ng-container>\r\n </table>\r\n </ng-container>\r\n</div>\r\n\r\n\r\n<!-- Emplacement visé par une colonne déplacée : trait orange sur la séparation des deux en-têtes. -->\r\n<div class=\"col-drop-indicator\" *ngIf=\"dropIndicator\"\r\n [ngStyle]=\"{ left: dropIndicator.left + 'px', top: dropIndicator.top + 'px', height: dropIndicator.height + 'px' }\"></div>\r\n\r\n<!-- Menu d'une colonne (flèche de l'en-tête ou clic droit sur une cellule), en trois zones séparées\r\n par un trait : Tri, Actions (celles du panneau des actions), Fonctions (sous-menu « Fonctions »). -->\r\n<div class=\"header-menu\" *ngIf=\"headerMenu\"\r\n [ngStyle]=\"{ top: headerMenu.y + 'px', left: headerMenu.x + 'px', visibility: headerMenu.ready ? 'visible' : 'hidden' }\"\r\n (click)=\"$event.stopPropagation()\" (contextmenu)=\"$event.preventDefault()\">\r\n <ul>\r\n <!-- Tri -->\r\n <li [class.checked]=\"this.facade.filter.isAsc(headerMenu.column.Property)\" (click)=\"sortColumn('asc')\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort-amount-asc\"></i><span>Trier par ordre croissant</span>\r\n </li>\r\n <li [class.checked]=\"this.facade.filter.isDesc(headerMenu.column.Property)\" (click)=\"sortColumn('desc')\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort-amount-desc\"></i><span>Trier par ordre décroissant</span>\r\n </li>\r\n <li [class.checked]=\"this.facade.filter.getSortList().length > 1\" (click)=\"openCustomSort()\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort\"></i><span>Tri personnalisé…</span>\r\n </li>\r\n <!-- Filtre sur la valeur de la cellule (clic droit sur une cellule) : première ligne des filtres composés. -->\r\n <li *ngIf=\"cellFilterValue() !== undefined\" (click)=\"filterOnCell()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-filter\"></i><span>Filtrer sur « {{ cellFilterLabel() }} »</span>\r\n </li>\r\n <!-- Affichage : vues personnalisées (cochée = affichée), puis « Modifier… ». -->\r\n <li *ngIf=\"this.facade.config.viewChooser\" class=\"has-submenu\" [class.open]=\"headerMenu.submenu === 'columns'\"\r\n (mouseenter)=\"setSubmenu('columns')\" (click)=\"setSubmenu('columns')\">\r\n <i class=\"fa fa-columns\"></i><span>Affichage</span><i class=\"fa fa-caret-right submenu-caret\"></i>\r\n\r\n <ul class=\"header-submenu header-submenu-columns\" *ngIf=\"headerMenu.submenu === 'columns'\"\r\n [class.left]=\"headerMenu.submenuLeft\" [class.up]=\"headerMenu.submenuUp\"\r\n (click)=\"$event.stopPropagation()\">\r\n <alg-grid-view-menu [chooser]=\"this.facade.config.viewChooser\" (done)=\"closeViewMenu()\" (loaded)=\"onViewMenuLoaded()\"></alg-grid-view-menu>\r\n </ul>\r\n </li>\r\n <!-- Colonnes figées à gauche, de la première jusqu'à celle-ci. -->\r\n <li [class.checked]=\"isFrozen(columnIndex(headerMenu.column))\" (click)=\"toggleFrozen()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-thumb-tack\"></i>\r\n <span>{{ frozenUntil === columnIndex(headerMenu.column) ? 'Libérer les colonnes' : 'Figer jusqu\\'à cette colonne' }}</span>\r\n </li>\r\n <li *ngIf=\"frozenUntil !== undefined && frozenUntil !== columnIndex(headerMenu.column)\"\r\n (click)=\"unfreezeColumns()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-unlock\"></i><span>Libérer les colonnes</span>\r\n </li>\r\n\r\n <!-- Actions -->\r\n <ng-container *ngIf=\"visibleActions.length > 0\">\r\n <li class=\"separator\"></li>\r\n <ng-container *ngFor=\"let action of visibleActions\">\r\n <ng-container *ngIf=\"{ disabled: (action.disabled$ | async) } as state\">\r\n <li class=\"menu-action\" [class.disabled]=\"state.disabled\"\r\n (click)=\"executeMenuAction(action, state.disabled)\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <img class=\"menu-action-icon\" [src]=\"getActionImageUrl(action.icon)\" [alt]=\"action.title\" />\r\n <span>{{ action.title }}</span>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- Affichage -->\r\n <li class=\"separator\"></li>\r\n <li class=\"has-submenu\" [class.open]=\"headerMenu.submenu === 'display'\"\r\n (mouseenter)=\"setSubmenu('display')\" (click)=\"setSubmenu('display')\">\r\n <i class=\"fa fa-calculator\"></i><span>Fonctions</span><i class=\"fa fa-caret-right submenu-caret\"></i>\r\n\r\n <ul class=\"header-submenu\" *ngIf=\"headerMenu.submenu === 'display'\"\r\n [class.left]=\"headerMenu.submenuLeft\" [class.up]=\"headerMenu.submenuUp\"\r\n (click)=\"$event.stopPropagation()\">\r\n <li *ngFor=\"let op of getAggregationOptions(headerMenu.column)\"\r\n [class.checked]=\"activeAggregations[headerMenu.column.Property] === op\"\r\n (click)=\"aggregateColumn(op)\">\r\n <i class=\"fa fa-check\"></i><span>{{ aggregationLabel(op) }}</span>\r\n </li>\r\n <ng-container *ngIf=\"activeAggregations[headerMenu.column.Property]\">\r\n <li class=\"separator\"></li>\r\n <li (click)=\"aggregateColumn(null)\">\r\n <i class=\"fa fa-times\"></i><span>Ne plus afficher</span>\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ul>\r\n</div>\r\n\r\n",
19133
+ template: "<div #datagridContainer [class]=\"this.datagridClass\" tabindex=\"-1\" (keydown)=\"onGridKeyDown($event)\" (scroll)=\"onGridScroll($event)\"\r\n (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\" (contextmenu)=\"openEmptyAreaContextMenu($event)\">\r\n <ng-container>\r\n <table class=\"data-grid-datatable\">\r\n <ng-container *ngIf=\"this.facade.style as ctx\">\r\n <ng-container *ngIf=\"this.facade.config.columns$ | async as rcolumns\">\r\n <thead>\r\n <tr>\r\n <th class=\"header row-state\" (click)=\"addFilterQuery()\"></th>\r\n <!-- En-tête : poignée de déplacement à gauche, intitulé, indicateur de tri,\r\n flèche du menu (tri, agrégation) à droite. Le clic ne trie plus. -->\r\n <th #thHeader *ngFor=\"let col of rcolumns;let i = index\"\r\n [attr.id]=\"guid.toString() + '-col-' + col.Property\"\r\n [attr.class]=\"'header' | gridStyle : ctx : { column : col }\"\r\n [style.width.px]=\"colWidth(col)\" [style.min-width.px]=\"colWidth(col)\" [style.max-width.px]=\"colWidth(col)\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\">\r\n <div class=\"header-cell\"\r\n [class.col-dragging]=\"draggedColumn === col.Property\">\r\n <span class=\"header-handle\" title=\"Déplacer la colonne\"\r\n (mousedown)=\"onHeaderMouseDown(col, $event)\">\r\n <i class=\"fa fa-ellipsis-v\"></i>\r\n </span>\r\n <label>{{ getHeaderLabel(col) }}</label>\r\n <i *ngIf=\"this.facade.filter.isAsc(col.Property)\" class=\"fa fa-long-arrow-up header-sort\" title=\"Tri croissant\"></i>\r\n <i *ngIf=\"this.facade.filter.isDesc(col.Property)\" class=\"fa fa-long-arrow-down header-sort\" title=\"Tri décroissant\"></i>\r\n <sup *ngIf=\"this.facade.filter.sortRank(col.Property) > 0\" class=\"header-sort-rank\"\r\n title=\"Rang dans le tri personnalisé\">{{ this.facade.filter.sortRank(col.Property) }}</sup>\r\n <i *ngIf=\"activeAggregations[col.Property]\" class=\"header-agg\" [title]=\"'Agrégation : ' + aggregationLabel(activeAggregations[col.Property])\">Σ</i>\r\n <!-- Dernière colonne suivie de la colonne vide de fin : sa flèche est dans la colonne vide. -->\r\n <button type=\"button\" class=\"header-menu-button\" title=\"Trier, agréger\"\r\n *ngIf=\"!(ctx.enableLastColumnResize && i === rcolumns.length - 1)\"\r\n [class.open]=\"headerMenu?.column.Property === col.Property\"\r\n (click)=\"toggleHeaderMenu(col, $event)\">\r\n <i class=\"fa fa-caret-down\"></i>\r\n </button>\r\n </div>\r\n <!-- Bord droit : largeur de la colonne (tirer), ajustée au contenu (double clic). -->\r\n <span class=\"col-resizer\" title=\"Largeur de la colonne (double clic : ajuster au contenu)\"\r\n (mousedown)=\"startResize(col, $event)\" (dblclick)=\"autoFitColumn(col, $event)\"\r\n (click)=\"$event.stopPropagation()\"></span>\r\n </th>\r\n <th *ngIf=\"ctx.enableLastColumnResize\" #lastColumnHeader class=\"header last-column\"\r\n (contextmenu)=\"openHeaderContextMenu($event, rcolumns[rcolumns.length - 1])\">\r\n <div class=\"header-cell header-cell-end\" *ngIf=\"rcolumns.length > 0\">\r\n <button type=\"button\" class=\"header-menu-button\" title=\"Trier, agréger\"\r\n [class.open]=\"headerMenu?.column.Property === rcolumns[rcolumns.length - 1].Property\"\r\n (click)=\"toggleHeaderMenu(rcolumns[rcolumns.length - 1], $event)\">\r\n <i class=\"fa fa-caret-down\"></i>\r\n </button>\r\n </div>\r\n </th>\r\n\r\n </tr>\r\n <ng-container *ngIf=\"this.facade.filter.additionalQuery$ | async as additionalQuery\">\r\n <tr #thFilterRows *ngFor=\"let filterRow of additionalQuery.Rows;let i = index\" >\r\n <th class=\"filter-header row-state\" (click)=\"removeFilterQuery(i)\"></th>\r\n <th class=\"filter-header\" [ngClass]=\"{disabled: !col.IsFilterEnabled}\" *ngFor=\"let col of rcolumns;let i = index\"\r\n [attr.id]=\"guid.toString() + '-filter-' + i\">\r\n <input *ngIf=\"col.IsFilterEnabled\" type=\"text\"\r\n class=\"filter-header-input\"\r\n name=\"keyword\"\r\n #uniquename=\"ngModel\"\r\n [attr.id]=\"guid.toString() + '-filter-input-' + col.Property\"\r\n [(ngModel)]=\"filterRow.ClausesByPropertyName.get(col.Property).FilterValue\"\r\n (ngModelChange)=\"handleFilterChanged($event, filterRow, col.Property)\"\r\n (change)=\"validateFilterChanged($event, filterRow, col.Property)\"\r\n />\r\n </th>\r\n <ng-container>\r\n <th #lastColumnFilter class=\"filter-header last-column\"></th>\r\n </ng-container>\r\n </tr>\r\n </ng-container>\r\n </thead>\r\n\r\n <tbody>\r\n <ng-container *ngIf=\"this.facade.data\">\r\n <ng-container *ngFor=\"let row of this.data.Values; let r = index;\">\r\n <tr\r\n [class]=\"'alternating' | gridStyle : ctx : { row : row, index : r, maxIndex : this.data.Values.length, hasAggregates: hasAggregates }\"\r\n [class.selected]=\"isRowSelected(row)\"\r\n [attr.id]=\"guid.toString() + '-row-' + row.Id\"\r\n (mousedown)=\"onRowMouseDown(row, $event)\"\r\n (mouseup)=\"onRowMouseUp(row, $event)\"\r\n (click)=\"onRowClick(row, $event)\"\r\n\r\n [rowRef]=\"guid + '-row-' + row.Id\"\r\n >\r\n\r\n <th [attr.class]=\"'rowstate' | gridStyle : ctx : { row : row }\">\r\n <ng-container *ngIf=\"selectionStyler.isModified(row) && !selectionStyler.isInvalid(row) && !selectionStyler.isWarning(row)\">\r\n <div class=\"new-rowstate\"><span>...</span></div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectionStyler.isInvalid(row)\">\r\n <div> <img class=\"error-row-icon\" src=\"assets/images/error.svg\" /> </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectionStyler.isWarning(row) && !selectionStyler.isInvalid(row)\">\r\n <div> <img class=\"error-row-icon\" src=\"assets/images/warning.svg\" /> </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"ctx.displayDetail\">\r\n <button type=\"button\" [attr.id]=\"row.Id + '_button'\" class=\"toggle-button btn\" (click)=\"toggleDetail(row)\">\r\n <i class=\"fa fa-chevron-right fa-custom\"></i>\r\n </button>\r\n </ng-container>\r\n </th>\r\n\r\n <td *ngFor=\"let column of rcolumns; index as i\"\r\n [attr.id]=\"cellId(row, column)\"\r\n [style.max-width.px]=\"colWidth(column)\" [attr.data-sized]=\"colWidth(column) ? '' : null\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\"\r\n [class]=\"'cell' | gridStyle : ctx : { row : row,column : column,index : r }\"\r\n [class.selected]=\"isRowSelected(row)\"\r\n (mousedown)=\"onCellMouseDown(r, i, $event)\"\r\n (mouseenter)=\"onCellMouseEnter(r, i, $event)\"\r\n (click)=\"onCellClick(row, column)\"\r\n (dblclick)=\"onCellDblClick(row, column)\">\r\n\r\n <alg-datagrid-cell #dataGridCellComponent\r\n class=\"gridcell-container\"\r\n [entity]=\"row\"\r\n [isEditable]=\"canEditCell(row, column)\"\r\n [property]=\"column.Property\"\r\n [managed]=\"true\"\r\n [mode]=\"showEditor(row, column) ? 'edit' : 'view'\"\r\n [class.grid-cursor]=\"isCursorCell(row, column)\"\r\n [class.grid-range]=\"isInRange(r, i)\"\r\n (contextmenu)=\"openContextMenu($event, row, column)\">\r\n\r\n <ng-template alg-editMode>\r\n <!-- Éditeur du champ du formulaire qui correspond à la colonne (sinon déduit de la colonne), à la hauteur de la ligne. -->\r\n <alg-field class=\"grid-editor\"\r\n [field]=\"getEditField(column)\"\r\n [form]=\"getRowForm(row)\"\r\n [showLabel]=\"false\"\r\n (propertyChanged)=\"onCellChanged(row, $event)\"\r\n >\r\n <!-- [focusable]=\"cellFocus\" -->\r\n </alg-field>\r\n </ng-template>\r\n <ng-template alg-viewMode>\r\n\r\n <div [ngSwitch]=\"column.Display?.type\" *ngIf=\"!isUndefined(row[column.Property])\">\r\n <div *ngSwitchCase=\"'url'\">\r\n <a [routerLink]=\"[$safeNavigationMigration(column.Display?.baseUrl),row[column.Property]]\">\r\n {{ row.Id || row[column.Property] }}\r\n </a>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'img'\" class=\"image\">\r\n <img *ngIf=\"column.Display?.imagePath?.length == 0\"\r\n [src]=\"(column.Display?.imgBase || '') + (column.Display?.sourceProperty ? row[column.Display?.sourceProperty] : row[column.Property])\" />\r\n\r\n <alg-image *ngIf=\"column.Display?.imagePath?.length != 0\"\r\n [imageUrl]=\"this.getEnumImageUrl(row,column)\" [imageHeight]=\"20\" [imageWidth]=\"20\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'img-file'\" class=\"image\">\r\n\r\n <alg-image\r\n [imageUrl]=\"this.getImageUrl(row,column)\" [wide]=\"true\" [imageHeight]=\"40\" [imageWidth]=\"40\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <div *ngSwitchCase=\"'wide-img'\" class=\"image\">\r\n <img *ngIf=\"column.Display?.imagePath?.length == 0\"\r\n [src]=\"(column.Display?.imgBase || '') + (column.Display?.sourceProperty ? row[column.Display?.sourceProperty] : row[column.Property])\" />\r\n\r\n <alg-image *ngIf=\"column.Display?.imagePath?.length != 0\" [wide]=\"true\"\r\n [imageUrl]=\"this.getEnumImageUrl(row,column)\" [imageHeight]=\"40\" [imageWidth]=\"40\">\r\n </alg-image>\r\n\r\n <div class=\"tool-tip\" *ngIf=\"!isUndefined(column.ToolTipProperty)\">\r\n {{ row[column.ToolTipProperty] }}\r\n </div>\r\n </div>\r\n\r\n <span *ngSwitchCase=\"'date'\">\r\n {{row[column.Property]| date: column.Display?.format || 'dd/MM/yyyy'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'boolean'\">{{row[column.Property] ? 'OUI' : 'NON'}} </span>\r\n\r\n <span *ngSwitchCase=\"'datetime'\">{{row[column.Property] | date: column.Display?.format || 'dd/MM/yyyy HH:mm:ss'}}</span>\r\n\r\n <span *ngSwitchCase=\"'money'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\"\r\n class=\"money\">\r\n {{\r\n row[column.Property]\r\n | currency: column.Display?.currency\r\n || (column.Display?.sourceProperty && row[column.Display?.sourceProperty] ? row[column.Display?.sourceProperty] : false)\r\n || 'EUR' : 'symbol'\r\n\r\n | todefine: $safeNavigationMigration(column.Display?.handleError)\r\n }}\r\n\r\n <div class=\"tool-tip\" *ngIf=\"ctx.showOnToolTip(row,column)\">\r\n {{ctx.showOnToolTip(row,column)}}\r\n </div>\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'percent'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] * $safeNavigationMigration(column.Display?.multiplier) | percent : column.Display?.format\r\n || '1.0-2' | todefine: $safeNavigationMigration(column.Display?.handleError)}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'number'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] * $safeNavigationMigration(column.Display?.multiplier) | number: column.Display?.format\r\n || '1.0-3' | todefine : $safeNavigationMigration(column.Display?.handleError)}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'fixedsizeinteger'\"\r\n [class.red]=\"!row[column.Property] && column.Display?.handleError\">\r\n {{row[column.Property] | tofixedsizeinteger : $safeNavigationMigration(column.Display?.completingCaracter) : $safeNavigationMigration(column.Display?.width) }}\r\n </span>\r\n <span *ngSwitchDefault [innerText]=\"column.Property == 'Id' ? (row[column.Property] < 0 ? '' : row[column.Property]) : row[column.Property]\">\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"isUndefined(row[column.Property])\">\r\n <span *ngIf=\"!isUndefined($safeNavigationMigration(column.Display?.nullCaseText))\" class=\"mw-invalid\">\r\n {{ column.Display?.nullCaseText }}\r\n </span>\r\n\r\n <span *ngIf=\"isUndefined($safeNavigationMigration(column.Display?.nullCaseText))\">\r\n &nbsp;&nbsp;&nbsp;\r\n </span>\r\n </div>\r\n\r\n </ng-template>\r\n\r\n </alg-datagrid-cell>\r\n </td>\r\n <ng-container *ngIf=\"ctx.enableLastColumnResize\">\r\n <!-- Colonne vide de fin : clic droit = menu de la dernière colonne. -->\r\n <td (contextmenu)=\"openContextMenu($event, row, rcolumns[rcolumns.length - 1])\"></td>\r\n </ng-container>\r\n </tr>\r\n </ng-container>\r\n </ng-container>\r\n </tbody>\r\n\r\n <tfoot>\r\n <tr class=\"empty-row\" *ngIf=\"hasAggregates\">\r\n <td></td>\r\n </tr>\r\n <tr class=\"aggregation-row\" *ngIf=\"hasAggregates\">\r\n <td class=\"row-state\"></td>\r\n <td *ngFor=\"let column of rcolumns; index as i\" [attr.class]=\"'cell' | gridStyle : ctx : { column : column, index : i }\"\r\n [attr.data-frozen]=\"isFrozen(i) ? '' : null\" [style.left.px]=\"frozenLeft(i)\">\r\n\r\n <ng-container *ngFor=\"let agg of columnAggregates[column.Property]\">\r\n\r\n <span class=\"agg-label\">{{ aggregationLabel(agg.op) }}</span>\r\n <span [ngSwitch]=\"agg.op === 'COUNT' ? 'count' : column.Display?.type\">\r\n\r\n <span *ngSwitchCase=\"'count'\">{{ agg.value | number: '1.0-0' }}</span>\r\n <span *ngSwitchCase=\"'percent'\">{{ agg.value | number: column.Display?.format || '1.0-3' }}</span>\r\n <span *ngSwitchCase=\"'fixedsizeinteger'\">{{ agg.value | number: column.Display?.format || '1.0-3' }}</span>\r\n\r\n <span *ngSwitchCase=\"'money'\" class=\"money\">\r\n {{ agg.value | currency: getColCurrency(column) }}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'date'\">\r\n {{agg.value | date: column.Display?.format || 'dd/MM/yyyy'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'datetime'\">\r\n {{agg.value | date: column.Display?.format || 'dd/MM/yyyy HH:mm:ss'}}\r\n </span>\r\n\r\n <span *ngSwitchCase=\"'number'\">\r\n {{agg.value | number: column.Display?.format || '1.0-3'}}\r\n </span>\r\n\r\n <span *ngSwitchDefault>\r\n {{agg.value}}\r\n </span>\r\n\r\n </span>\r\n\r\n </ng-container>\r\n\r\n </td>\r\n\r\n <ng-container *ngIf=\"ctx.enableLastColumnResize\">\r\n <td class=\"last-column\"></td>\r\n </ng-container>\r\n\r\n </tr>\r\n </tfoot>\r\n </ng-container>\r\n </ng-container>\r\n </table>\r\n </ng-container>\r\n</div>\r\n\r\n\r\n<!-- Emplacement visé par une colonne déplacée : trait orange sur la séparation des deux en-têtes. -->\r\n<div class=\"col-drop-indicator\" *ngIf=\"dropIndicator\"\r\n [ngStyle]=\"{ left: dropIndicator.left + 'px', top: dropIndicator.top + 'px', height: dropIndicator.height + 'px' }\"></div>\r\n\r\n<!-- Menu d'une colonne (flèche de l'en-tête ou clic droit sur une cellule), en trois zones séparées\r\n par un trait : Tri, Actions (celles du panneau des actions), Fonctions (sous-menu « Fonctions »). -->\r\n<div class=\"header-menu\" *ngIf=\"headerMenu\"\r\n [ngStyle]=\"{ top: headerMenu.y + 'px', left: headerMenu.x + 'px', visibility: headerMenu.ready ? 'visible' : 'hidden' }\"\r\n (click)=\"$event.stopPropagation()\" (contextmenu)=\"$event.preventDefault()\">\r\n <ul>\r\n <!-- Tri -->\r\n <li [class.checked]=\"this.facade.filter.isAsc(headerMenu.column.Property)\" (click)=\"sortColumn('asc')\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort-amount-asc\"></i><span>Trier par ordre croissant</span>\r\n </li>\r\n <li [class.checked]=\"this.facade.filter.isDesc(headerMenu.column.Property)\" (click)=\"sortColumn('desc')\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort-amount-desc\"></i><span>Trier par ordre décroissant</span>\r\n </li>\r\n <li [class.checked]=\"this.facade.filter.getSortList().length > 1\" (click)=\"openCustomSort()\"\r\n (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-sort\"></i><span>Tri personnalisé…</span>\r\n </li>\r\n <!-- Filtre sur la valeur de la cellule (clic droit sur une cellule) : première ligne des filtres composés. -->\r\n <li *ngIf=\"cellFilterValue() !== undefined\" (click)=\"filterOnCell()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-filter\"></i><span>Filtrer sur « {{ cellFilterLabel() }} »</span>\r\n </li>\r\n <!-- Affichage : vues personnalisées (cochée = affichée), puis « Modifier… ». -->\r\n <li *ngIf=\"this.facade.config.viewChooser\" class=\"has-submenu\" [class.open]=\"headerMenu.submenu === 'columns'\"\r\n (mouseenter)=\"setSubmenu('columns')\" (click)=\"setSubmenu('columns')\">\r\n <i class=\"fa fa-columns\"></i><span>Affichage</span><i class=\"fa fa-caret-right submenu-caret\"></i>\r\n\r\n <ul class=\"header-submenu header-submenu-columns\" *ngIf=\"headerMenu.submenu === 'columns'\"\r\n [class.left]=\"headerMenu.submenuLeft\" [class.up]=\"headerMenu.submenuUp\"\r\n (click)=\"$event.stopPropagation()\">\r\n <alg-grid-view-menu [chooser]=\"this.facade.config.viewChooser\" (done)=\"closeViewMenu()\" (loaded)=\"onViewMenuLoaded()\"></alg-grid-view-menu>\r\n </ul>\r\n </li>\r\n <!-- Colonnes figées à gauche, de la première jusqu'à celle-ci. -->\r\n <li [class.checked]=\"isFrozen(columnIndex(headerMenu.column))\" (click)=\"toggleFrozen()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-thumb-tack\"></i>\r\n <span>{{ frozenUntil === columnIndex(headerMenu.column) ? 'Libérer les colonnes' : 'Figer jusqu\\'à cette colonne' }}</span>\r\n </li>\r\n <li *ngIf=\"frozenUntil !== undefined && frozenUntil !== columnIndex(headerMenu.column)\"\r\n (click)=\"unfreezeColumns()\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <i class=\"fa fa-unlock\"></i><span>Libérer les colonnes</span>\r\n </li>\r\n\r\n <!-- Actions -->\r\n <ng-container *ngIf=\"visibleActions.length > 0\">\r\n <li class=\"separator\"></li>\r\n <ng-container *ngFor=\"let action of visibleActions\">\r\n <ng-container *ngIf=\"{ disabled: (action.disabled$ | async) } as state\">\r\n <li class=\"menu-action\" [class.disabled]=\"state.disabled\"\r\n (click)=\"executeMenuAction(action, state.disabled)\" (mouseenter)=\"setSubmenu(undefined)\">\r\n <img class=\"menu-action-icon\" [src]=\"getActionImageUrl(action.icon)\" [alt]=\"action.title\" />\r\n <span>{{ action.title }}</span>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- Affichage -->\r\n <li class=\"separator\"></li>\r\n <li class=\"has-submenu\" [class.open]=\"headerMenu.submenu === 'display'\"\r\n (mouseenter)=\"setSubmenu('display')\" (click)=\"setSubmenu('display')\">\r\n <i class=\"fa fa-calculator\"></i><span>Fonctions</span><i class=\"fa fa-caret-right submenu-caret\"></i>\r\n\r\n <ul class=\"header-submenu\" *ngIf=\"headerMenu.submenu === 'display'\"\r\n [class.left]=\"headerMenu.submenuLeft\" [class.up]=\"headerMenu.submenuUp\"\r\n (click)=\"$event.stopPropagation()\">\r\n <li *ngFor=\"let op of getAggregationOptions(headerMenu.column)\"\r\n [class.checked]=\"activeAggregations[headerMenu.column.Property] === op\"\r\n (click)=\"aggregateColumn(op)\">\r\n <i class=\"fa fa-check\"></i><span>{{ aggregationLabel(op) }}</span>\r\n </li>\r\n <ng-container *ngIf=\"activeAggregations[headerMenu.column.Property]\">\r\n <li class=\"separator\"></li>\r\n <li (click)=\"aggregateColumn(null)\">\r\n <i class=\"fa fa-times\"></i><span>Ne plus afficher</span>\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ul>\r\n</div>\r\n\r\n",
19010
19134
  styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{flex:1}.header.last-column{border-left:none!important;width:100%}table thead .sorting,table thead .sorting_asc,table thead .sorting_desc,table thead .sorting_asc_disabled,table thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table thead th:first-child,table tfoot tr:first-child{position:sticky;left:0;z-index:2}table tbody th:first-child{position:sticky;left:0;z-index:1;border-right:none}table tbody th:first-child div{text-align:center;vertical-align:middle;line-height:20px}table tbody tr.row-odd th:first-child{background:#ebf0ff}table tbody tr.selected.row-odd.mw-invalid th:first-child,table tbody tr.selected.row-even.mw-invalid th:first-child,table tbody tr.selected.row-odd.mw-warning th:first-child,table tbody tr.selected.row-even.mw-warning th:first-child,table tbody tr.selected.row-even th:first-child,table tbody tr.selected.row-odd th:first-child{background-color:#8ea7f8;color:#fff}table tbody tr.row-odd.mw-invalid th:first-child,table tbody tr.row-even.mw-invalid th:first-child{background-color:#f2dede}table tbody tr.row-odd.mw-warning th:first-child,table tbody tr.row-even.mw-warning th:first-child{background-color:#fff4de}table thead th{position:-webkit-sticky;position:sticky;top:0;z-index:1;background:#fff;border-left:1px solid #3C5DC4;border-bottom:1px solid #3C5DC4}table thead .sorting{background-image:none;cursor:pointer}table thead .sorting_asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMTgwMTE3NDA3MjA2ODExQjM4MkY2QzVGRUYwRTJDNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4MkFEQzYxNjIyQzExMUUxQTFGMUFEQUQ1QjJBNTM4QyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4MkFEQzYxNTIyQzExMUUxQTFGMUFEQUQ1QjJBNTM4QyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTFCMzgyRjZDNUZFRjBFMkM0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjAxODAxMTc0MDcyMDY4MTFCMzgyRjZDNUZFRjBFMkM0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+z5ABTAAAAI5JREFUeNpi/P//PwO1ABMDFQELIQXVjfe4gFQbEJe11iv9otRl2UCcBcSphBQy4gszoKu0gNROIJYB4jtA7AF03V2SXQY0iBFIFUMNAgEVIM6DipPsTQcgDkQTSwRia5IMA9rOC6RqgVgQTQokXgOUZyfFZSFQF/zAgh2BOIjkCBjQRDtq2Khh9DAMIMAAT9AmNBDSXegAAAAASUVORK5CYII=);cursor:pointer}table thead .sorting_desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMTgwMTE3NDA3MjA2ODExQjM4MkY2QzVGRUYwRTJDNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4MkFEQzYxQTIyQzExMUUxQTFGMUFEQUQ1QjJBNTM4QyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4MkFEQzYxOTIyQzExMUUxQTFGMUFEQUQ1QjJBNTM4QyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTFCMzgyRjZDNUZFRjBFMkM0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjAxODAxMTc0MDcyMDY4MTFCMzgyRjZDNUZFRjBFMkM0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+1fsfwAAAAJdJREFUeNpi/P//PwO1ABMDFcGoYaOG0cMwFmyC1Y33IoHUAiD+h8MBGa31SvOJddk6IN4PxBxY8FEgXkO0N4G2/gRSLUD8GU3qPRA3A+U/kxpmIBege2U9EB/ApYERX6kBDDtlILUDiFWA+AkQuwNddY2s2ARqvAukJgDxbyCehM8gnLGJBmYDsSoQTyWkkHHQFo4AAQYAAA0piq4hbqwAAAAASUVORK5CYII=);cursor:pointer}table thead .sorting_asc_disabled,table thead .sorting_desc_disabled{background-image:none}.checkbok-container{align-items:center;margin:0;padding:0}.gridcell-container{padding:0;min-width:fit-content}.gridcell-container input[type=number]{display:block;size:1;font-size:10pt;width:100%!important;height:100%;margin:0!important;border:2px solid #F78383}.datagrid-custom-container{height:100%;overflow:auto;margin-left:-1px}.data-grid-datatable{table-layout:fixed;cursor:pointer;border-spacing:0px}.data-grid-datatable td{box-shadow:none;padding:2px 5px;white-space:nowrap}.data-grid-datatable td.row-state{padding:2px 5px;width:16px}.data-grid-datatable td.img{padding:2px 5px}.data-grid-datatable td.row-state img{height:16px;width:16px;margin-top:-3px;padding:0}.data-grid-datatable tr{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.header{vertical-align:bottom;padding:2px 5px 2px 0;font-weight:400;color:#3c5dc4;white-space:nowrap;line-height:25px;min-width:32px;margin-bottom:5px}.header.img{min-width:16px}.header.row-state{min-width:27px;position:-webkit-sticky;position:sticky;top:0;left:0;z-index:2;background:#fff;border-left:none;border-right:none}.filter-header{background-color:#f7f7f7!important}.filter-header.disabled{background-color:#9fa9c7!important}.filter-header.row-state:hover:before{position:relative;display:block;float:left;width:16px;height:16px;content:\" \";background-size:16px 16px;-webkit-background-size:16px 16px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"57\" height=\"57\" viewBox=\"0 0 57 57\">%0D%0A <path id=\"Rond_Croix\" data-name=\"Rond Croix\" d=\"M28.5,57A28.5,28.5,0,0,1,8.347,8.347,28.5,28.5,0,1,1,48.652,48.652,28.317,28.317,0,0,1,28.5,57Zm0-24.036h0l11.9,11.9L44.869,40.4,32.964,28.5,44.869,16.593,40.4,12.129,28.5,24.034,16.594,12.129l-4.465,4.464L24.034,28.5,12.129,40.4l4.463,4.465L28.5,32.965Z\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 0 0 3px;filter:brightness(0) saturate(100%) invert(70%) sepia(93%) saturate(1540%) hue-rotate(309deg) brightness(105%) contrast(94%)}.filter-header-input{height:calc(100% - 2px);width:calc(100% - 2px);margin:0;border:none!important;background-color:#f7f7f7;padding:0 0 0 5px;color:#4d5a81}.header.row-state:before{position:relative;display:block;float:left;width:16px;height:16px;content:\" \";background-size:16px 16px;-webkit-background-size:16px 16px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->%0D%0A<path fill=\"currentColor\" d=\"M3.9 54.9C10.5 40.9 24.5 32 40 32l432 0c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9 320 448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6l0-79.1L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z\"/></svg>');background-repeat:no-repeat;margin:0 0 3px 5px;filter:brightness(0) saturate(100%) invert(56%) sepia(79%) saturate(2301%) hue-rotate(202deg) brightness(92%) contrast(91%)}.filter-header.last-column{border-left:none!important}.header label{cursor:pointer;margin-bottom:0;margin-right:10px;padding-left:5px;width:100%}.row-odd{background-color:#ebf0ff;border-left:1px solid #C1D0FD;border-bottom:1px solid #C1D0FD}.row-even{background-color:#f7faff;border-left:1px solid #C1D0FD;border-bottom:1px solid #C1D0FD}.error-row-icon{padding:2px;width:20px}.selected{background-color:#8ea7f8;color:#fff}.mw-invalid{font-weight:600;background-color:#f2dede;border:1px solid var(--red-error)}.mw-invalid td{background-color:#f2dede!important}.data-grid-datatable tbody tr.mw-invalid>td,.data-grid-datatable tbody tr.mw-invalid>th{box-shadow:inset 0 1px 0 var(--red-error),inset 0 -1px 0 var(--red-error)}span.mw-invalid{padding:0 3px;color:var(--red-error);background-color:transparent}.mw-warning.selected,.mw-invalid.selected{background-color:#8ea7f8}.mw-warning.selected td,.mw-invalid.selected td{background-color:#8ea7f8!important}.mw-warning{font-weight:600;background-color:#fff4de;border:1px solid var(--orange-algorima)}.mw-warning td{background-color:#fff4de!important}.mw-modified{font-weight:600;border:1px solid var(--main-bg-color)}.first-row{border-top:none}.last-row,.last-row td{border-bottom:none!important}.data-grid-datatable>tbody>tr{z-index:50}.image{position:relative;display:flex;flex-direction:row}.image img{height:20px;width:auto}td.money{position:relative}.tool-tip{visibility:hidden;background-color:#424242;color:#fff;text-align:left;border-radius:3px;font-size:15px;position:absolute;z-index:1;top:-10%;left:150%;opacity:0;transition:opacity .3s;padding:5px 15px;white-space:nowrap;overflow:visible}.tool-tip:after{content:\" \";position:absolute;top:50%;right:100%;margin-top:-5px;border-width:5px;border-style:solid;border-color:transparent rgb(66,66,66) transparent transparent}.image:hover .tool-tip{visibility:visible;opacity:1}td.money:hover .tool-tip{visibility:visible;opacity:1;left:100%;height:28px;font-size:13px;margin-top:0;padding:5px 10px 10px}.disabled{pointer-events:none}.edit-mode{background-color:#fff;border:1px solid #4465C9;min-width:90px!important;height:30px!important}.edit-mode .form-group{min-height:0px}.edit-mode .inactivated{background-color:#dbe4ff80}.edit-mode .inactivated-fo{background-color:#dbe4ff}.edit-mode .ecartclass{border:2px solid #4465C9}.edit-mode .error-cell{font-weight:700;color:#f78383!important}.edit-mode .selected{color:#4d5a81}.highlight-1,.highlight-1.selected,.edit-mode.highlight-1.selected{background-color:#4d5a81!important;color:#fff!important}.highlight-2,.highlight-2.selected,.edit-mode.highlight-2.selected{background-color:#dbe4ff!important;color:#4d5a81!important}.highlight-3,.highlight-3.selected,.edit-mode.highlight-3.selected{background-color:#4465c9!important;color:#fff!important}.highlight-4,.highlight-4.selected,.edit-mode.highlight-4.selected{background-color:#f78383!important;color:#fff!important}.highlight-5,.highlight-5.selected{background-color:#4465c9!important;color:#fff}.fixed-column{position:-webkit-sticky;position:sticky;left:27px;z-index:2!important;border-left:none}.fixed-header{position:-webkit-sticky;position:sticky;left:27px;z-index:3!important;background:#fff}.has-focus{padding:0!important}.has-focus ::ng-deep input,.has-focus ::ng-deep input:focus-visible,.has-focus ::ng-deep input:focus{border-radius:.1;outline:none!important;width:100%;height:29px;min-height:0;margin:0;border:1px solid #4465C9}.has-focus ::ng-deep .form-group{margin-bottom:0;min-height:0}.has-focus ::ng-deep .field-container{margin-bottom:0}.edit-mode.has-focus{width:90px!important}.bordered{border-left:1px solid #14358f!important;border-right:1px solid #14358f!important}.match-keywords{color:var(--main-bg-color);font-weight:700}.datagrid-img img{height:20px;width:20px}.right-align{text-align:right}.left-align{text-align:left}.detail-div{height:0px;overflow:hidden;transition:all .2s linear;width:100%}.fa-custom{font-size:smaller}.detail-div-show{height:150px;transition:all .2s linear}.toggleable{visibility:collapse}.toggleable-show{visibility:visible}.toggle-button-down{transform:rotate(90deg);transition:all .2s linear}.no-result{color:#5272d3;height:100%;width:100%;padding:15px 15px 150px}.new-rowstate{display:flex;flex-direction:row;align-items:center;align-self:center;height:100%;width:100%}.new-rowstate span{height:100%;width:100%;margin-top:-6px}.context-menu{position:absolute;background:#ffffff26;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.2);box-shadow:0 4px 12px #0000004d;padding:8px;z-index:1000;min-width:75px;max-width:250px;transition:all .3s ease-in-out}.context-menu ul{list-style:none;margin:0;padding:0}.context-menu li{padding:10px 14px;cursor:pointer;border-radius:8px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}.context-menu li:hover{background:linear-gradient(135deg,#ffffff4d,#ffffff1a);transform:scale(1.02)}.context-menu li:active{transform:scale(.98)}.grid-container{overflow:auto;position:relative}.aggregation-row{position:sticky;bottom:0;z-index:50;background:#f7faff!important;background-color:#f7faff!important;font-weight:700;border-top:1px solid #4D5A81!important;border-bottom:1px solid #4D5A81!important}.aggregation-row td{background:#f7faff!important;background-color:#f7faff!important;border-top:1px solid #4D5A81!important;border-bottom:1px solid #4D5A81!important;border-left:none!important;border-right:none!important}.empty-row td{padding:0}.col-resizer{position:absolute;top:0;right:-5px;z-index:4;width:10px;height:100%;cursor:col-resize}.col-resizer:hover{background:#3c5dc440}.data-grid-datatable td[data-sized]{overflow:hidden;text-overflow:ellipsis}:where(.data-grid-datatable tbody tr,.data-grid-datatable tfoot tr){background-color:#fff}.data-grid-datatable td[data-frozen]{position:sticky;z-index:1;background-color:inherit}.data-grid-datatable thead th[data-frozen]{position:sticky;z-index:3}.data-grid-datatable td[data-frozen]:has(+td:not([data-frozen])),.data-grid-datatable thead th[data-frozen]:has(+th:not([data-frozen])){box-shadow:inset -2px 0 #3c5dc459}table thead th.header{background-image:none!important;cursor:default}.header-cell{position:relative;display:flex;align-items:center;gap:2px}.header-cell label{flex:1 1 auto;cursor:default;margin:0;padding-left:2px;overflow:hidden;text-overflow:ellipsis}.header-cell.col-dragging{opacity:.8}.dg-column-ghost{border:1px solid #C1D0FD;box-sizing:border-box}.col-drop-indicator{position:fixed;z-index:3100;width:1px;border-radius:0;background:var(--orange-algorima, #f59d0f);pointer-events:none}.header-cell-end{justify-content:flex-end;align-items:center;height:25px}.header-handle{flex-shrink:0;display:flex;align-items:center;justify-content:center;box-sizing:content-box;width:17px;height:20px;padding:0 2px;color:#3c5dc4;opacity:.35;cursor:grab}.header-handle:hover{opacity:1}.header-handle:active{cursor:grabbing}.header-sort,.header-agg{flex-shrink:0;color:#3c5dc4;font-size:11px;font-style:normal}.header-menu-button{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:16px;height:18px;padding:0;border:0;border-radius:3px;background:none;color:#3c5dc4;opacity:.5;cursor:pointer}.header-menu-button:hover,.header-menu-button.open{opacity:1;background:#4465c926}.header-menu{position:fixed;z-index:3000;width:230px;padding:4px 0;background:#fff;border:1px solid #DBE4FF;border-radius:5px;box-shadow:0 4px 14px #0003}.header-menu ul{margin:0;padding:0;list-style:none}.header-menu li{display:flex;align-items:center;gap:8px;padding:5px 12px;color:#3c5dc4;font-size:12.5px;cursor:pointer}.header-menu li i{width:14px;text-align:center;visibility:visible}.header-menu li:hover{background:#dbe4ff}.header-menu li.checked{font-weight:600}.header-menu li.separator{height:1px;margin:4px 0;padding:0;background:#dbe4ff;cursor:default}.header-menu li .fa-check{visibility:hidden}.header-menu li.checked>.fa-check{visibility:visible}.header-menu li.menu-action.disabled{opacity:.45;cursor:default}.header-menu li.menu-action.disabled:hover{background:none}.header-menu .menu-action-icon{width:14px;height:14px}.header-menu li.has-submenu{position:relative}.header-menu li.has-submenu.open{background:#dbe4ff}.header-menu li.has-submenu .submenu-caret{margin-left:auto}.header-menu .header-submenu{position:absolute;top:-5px;left:100%;width:200px;padding:4px 0;background:#fff;border:1px solid #DBE4FF;border-radius:5px;box-shadow:0 4px 14px #0003;font-weight:400}.header-menu .header-submenu.left{left:auto;right:100%}.header-menu .header-submenu.header-submenu-columns{width:240px;max-height:320px;overflow-y:auto}.header-menu .header-submenu .column-choice input[type=checkbox]{width:14px;margin:0;pointer-events:none}.header-menu .header-submenu .submenu-loading{opacity:.6;cursor:default}.header-menu .header-submenu .view-lock{margin-left:auto;padding-left:8px;opacity:.5;font-size:11px}.header-menu .header-submenu.up{top:auto;bottom:-5px}.header-sort-rank{flex-shrink:0;color:#3c5dc4;font-size:9px}.aggregation-row .agg-label{margin-right:4px;color:#3c5dc4;font-size:10.5px;opacity:.7}.datagrid-custom-container:focus,.datagrid-custom-container:focus-visible{outline:none}.data-grid-datatable td .image ::ng-deep img{display:block}.data-grid-datatable td:has(>.grid-cursor){outline:2px solid #4465C9;outline-offset:-2px}.data-grid-datatable td:has(>.grid-range){background-color:#4465c940!important;box-shadow:inset 0 0 0 1px #4465c980;color:#4d5a81!important}.data-grid-datatable td:has(>.grid-range) ::ng-deep *{color:#4d5a81!important}.data-grid-datatable td:has(.grid-editor){padding:0!important;background-color:#fff}.grid-editor{display:block;height:23px;overflow:visible;color:#4d5a81}.grid-editor ::ng-deep .field-container,.grid-editor ::ng-deep .form-group,.grid-editor ::ng-deep .form-group>div,.grid-editor ::ng-deep .checkbox-container{margin:0!important;padding:0;min-height:0!important;height:23px}.grid-editor ::ng-deep .validation-message,.grid-editor ::ng-deep .checkbox-label{display:none}.grid-editor ::ng-deep input,.grid-editor ::ng-deep input:focus,.grid-editor ::ng-deep textarea{box-sizing:border-box;width:100%;height:23px!important;min-height:0!important;margin:0!important;padding:0 4px;border:1px solid #4465C9;border-radius:0;box-shadow:none;font-size:inherit;line-height:normal;resize:none}.grid-editor ::ng-deep input.ng-invalid.ng-touched,.grid-editor ::ng-deep input.ng-invalid.ng-dirty{border-color:#f78383}.grid-editor ::ng-deep input[type=checkbox]{width:auto}.grid-editor ::ng-deep .ng-select .ng-select-container{color:#4d5a81;min-height:0!important;height:23px!important;border-radius:0;border-color:#4465c9}.grid-editor ::ng-deep .ng-select.ng-select-single .ng-select-container .ng-value-container .ng-input{top:0}.grid-editor ::ng-deep .ng-select .ng-select-container .ng-value-container{padding-top:0}.grid-editor ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-input>input{height:100%!important;border:none}.grid-editor ::ng-deep .form-group>div{flex:1;min-width:0;width:100%}.grid-editor ::ng-deep .ng-select{width:100%}.grid-editor ::ng-deep alg-selector{display:flex;width:100%;height:23px}.grid-editor ::ng-deep .input-select-default,.grid-editor ::ng-deep .input-select-default.with-button{flex:1;min-width:0;padding-right:0;margin-right:0}.grid-editor ::ng-deep .input-group-btn{float:none}.grid-editor ::ng-deep .btn,.grid-editor ::ng-deep .btn-32,.grid-editor ::ng-deep .btn-input-select{height:23px;width:23px;padding:0;line-height:1;border-radius:0}.grid-editor ::ng-deep .input-with-button{display:flex;height:23px}.grid-editor ::ng-deep .switch{margin:2px 0 0 4px;transform:scale(.7);transform-origin:left top}::ng-deep body.dg-column-dragging,::ng-deep body.dg-column-dragging *{cursor:grabbing!important;-webkit-user-select:none!important;user-select:none!important}\n"]
19011
19135
  }]
19012
19136
  }],
@@ -19567,330 +19691,827 @@ i0.ɵɵngDeclareClassMetadata({
19567
19691
  }]
19568
19692
  }
19569
19693
  });
19570
- var FilterSummaryPanelComponent = class FilterSummaryPanelComponent {
19571
- isEmptyFilter() {
19572
- return this.filterList.length == 0;
19694
+ const FilterOperators = {
19695
+ text: [
19696
+ {
19697
+ value: "contains",
19698
+ label: "contient"
19699
+ },
19700
+ {
19701
+ value: "notcontains",
19702
+ label: "ne contient pas"
19703
+ },
19704
+ {
19705
+ value: "startswith",
19706
+ label: "commence par"
19707
+ },
19708
+ {
19709
+ value: "endswith",
19710
+ label: "finit par"
19711
+ },
19712
+ {
19713
+ value: "eq",
19714
+ label: "est égal à"
19715
+ },
19716
+ {
19717
+ value: "ne",
19718
+ label: "est différent de"
19719
+ },
19720
+ {
19721
+ value: "isnull",
19722
+ label: "est vide"
19723
+ },
19724
+ {
19725
+ value: "notnull",
19726
+ label: "n'est pas vide"
19727
+ }
19728
+ ],
19729
+ number: [
19730
+ {
19731
+ value: "eq",
19732
+ label: "="
19733
+ },
19734
+ {
19735
+ value: "ne",
19736
+ label: "≠"
19737
+ },
19738
+ {
19739
+ value: "gt",
19740
+ label: ">"
19741
+ },
19742
+ {
19743
+ value: "ge",
19744
+ label: "≥"
19745
+ },
19746
+ {
19747
+ value: "lt",
19748
+ label: "<"
19749
+ },
19750
+ {
19751
+ value: "le",
19752
+ label: "≤"
19753
+ },
19754
+ {
19755
+ value: "between",
19756
+ label: "entre"
19757
+ },
19758
+ {
19759
+ value: "isnull",
19760
+ label: "est vide"
19761
+ },
19762
+ {
19763
+ value: "notnull",
19764
+ label: "n'est pas vide"
19765
+ }
19766
+ ],
19767
+ date: [
19768
+ {
19769
+ value: "on",
19770
+ label: "le"
19771
+ },
19772
+ {
19773
+ value: "before",
19774
+ label: "avant le"
19775
+ },
19776
+ {
19777
+ value: "after",
19778
+ label: "après le"
19779
+ },
19780
+ {
19781
+ value: "datebetween",
19782
+ label: "entre"
19783
+ },
19784
+ {
19785
+ value: "today",
19786
+ label: "aujourd'hui"
19787
+ },
19788
+ {
19789
+ value: "thismonth",
19790
+ label: "ce mois-ci"
19791
+ },
19792
+ {
19793
+ value: "isnull",
19794
+ label: "est vide"
19795
+ },
19796
+ {
19797
+ value: "notnull",
19798
+ label: "n'est pas vide"
19799
+ }
19800
+ ],
19801
+ bool: [{
19802
+ value: "true",
19803
+ label: "est oui"
19804
+ }, {
19805
+ value: "false",
19806
+ label: "est non"
19807
+ }],
19808
+ list: [{
19809
+ value: "in",
19810
+ label: "est l'une de"
19811
+ }, {
19812
+ value: "notin",
19813
+ label: "n'est aucune de"
19814
+ }]
19815
+ };
19816
+ var FilterEditorComponent = class FilterEditorComponent {
19817
+ constructor() {
19818
+ this.columns = [];
19819
+ this.filter = {
19820
+ Logic: "and",
19821
+ Conditions: [],
19822
+ Groups: []
19823
+ };
19824
+ this.sort = [];
19825
+ this.embedded = false;
19826
+ this.section = null;
19827
+ this.panelFields = [];
19828
+ this.addedPanelKeys = [];
19829
+ this.viewLabel = "";
19830
+ this.canSaveInView = true;
19831
+ this.saveInView = false;
19832
+ this.tab = "filters";
19833
+ this.maxDepth = 3;
19834
+ this.trackIndex = (index) => index;
19573
19835
  }
19574
- constructor(validationService, bus, entityFacadeRegistryService) {
19575
- this.validationService = validationService;
19576
- this.bus = bus;
19577
- this.entityFacadeRegistryService = entityFacadeRegistryService;
19578
- this.filterList = [];
19579
- this.filterType = FilterType;
19580
- this.Fields = [];
19581
- this.DisplayFields = this.Fields;
19582
- this.currentCriteria = null;
19583
- this.hiddenFilters = [];
19584
- this.destroy$ = new Subject();
19836
+ trackKey(_, field) {
19837
+ return field.key;
19585
19838
  }
19586
- ngOnInit() {
19587
- this.facade = this.entityFacadeRegistryService.getFacade(this.entityKey, this.guid);
19588
- this.wrapper = new FormWrapper(this.validationService, this.bus, false);
19589
- combineLatest([
19590
- this.facade.criteria$,
19591
- this.facade.config.filterDescriptors$,
19592
- this.facade.layout.hiddenSummaryFilters$
19593
- ]).pipe(takeUntil(this.destroy$)).subscribe(([vm, fields, hidden]) => {
19594
- const filters = fields.filter((x) => x.PropertyName != "Keywords" && x.PropertyName != "Id");
19595
- this.Fields = filters;
19596
- this.hiddenFilters = hidden;
19597
- this.currentCriteria = vm;
19598
- this.wrapper.initialize(filters);
19599
- this.wrapper.hookFormEntity(vm);
19600
- this.updateSummary();
19601
- });
19839
+ get panel() {
19840
+ return this.filter.Panel ??= {};
19602
19841
  }
19603
- ngOnDestroy() {
19604
- this.destroy$.next();
19605
- this.destroy$.complete();
19842
+ shownPanelFields() {
19843
+ const keys = /* @__PURE__ */ new Set([...Object.keys(this.filter.Panel ?? {}), ...this.addedPanelKeys]);
19844
+ const known = this.panelFields.filter((f) => keys.has(f.key));
19845
+ const unknown = [...keys].filter((k) => !this.panelFields.some((f) => f.key === k)).map((k) => ({
19846
+ key: k,
19847
+ label: k,
19848
+ type: "text"
19849
+ }));
19850
+ return [...known, ...unknown];
19606
19851
  }
19607
- updateSummary() {
19608
- const descriptors = this.Fields;
19609
- if (descriptors) {
19610
- this.filterList.splice(0, this.filterList.length);
19611
- descriptors.filter((field) => !this.hiddenFilters.includes(field.PropertyName)).forEach((field) => {
19612
- this.updateFieldSummary(field);
19613
- });
19852
+ availablePanelFields() {
19853
+ const shown = new Set(this.shownPanelFields().map((f) => f.key));
19854
+ return this.panelFields.filter((f) => !shown.has(f.key));
19855
+ }
19856
+ addPanelField(key) {
19857
+ if (key && !this.addedPanelKeys.includes(key)) this.addedPanelKeys = [...this.addedPanelKeys, key];
19858
+ }
19859
+ removePanelField(field) {
19860
+ this.addedPanelKeys = this.addedPanelKeys.filter((k) => k !== field.key);
19861
+ if (this.filter.Panel) delete this.filter.Panel[field.key];
19862
+ }
19863
+ panelValue(field) {
19864
+ const value = this.filter.Panel?.[field.key];
19865
+ return value === null || value === void 0 ? "" : field.type === "date" ? String(value).substring(0, 10) : String(value);
19866
+ }
19867
+ setPanelValue(field, raw) {
19868
+ const panel = this.panel;
19869
+ if (raw === "" || raw === null || raw === void 0) {
19870
+ delete panel[field.key];
19871
+ return;
19614
19872
  }
19873
+ if (field.type === "bool") panel[field.key] = raw === true || raw === "true";
19874
+ else if (field.type === "number" || field.type === "list" && /^-?\d+$/.test(String(raw))) panel[field.key] = Number(raw);
19875
+ else panel[field.key] = raw;
19615
19876
  }
19616
- updateFieldSummary(field) {
19617
- const displayedVal = this.getFilterSummaryValue(field);
19618
- if (displayedVal !== null && displayedVal !== void 0) this.filterList.push({
19619
- DisplayedValue: displayedVal,
19620
- Field: field
19621
- });
19877
+ get shownTab() {
19878
+ return this.section ?? this.tab;
19622
19879
  }
19623
- getFilterSummaryValue(field) {
19624
- if (!field) return null;
19625
- const fv = this.currentCriteria != null ? this.currentCriteria[field.PropertyName] : this.wrapper.form.get(field.PropertyName)?.value;
19626
- if (Utilities.IsNil(fv) || fv === -1 || parseInt(fv) === -1 || Utilities.isEmptyString(fv)) return;
19627
- let displayedValue = null;
19628
- if (field.FilterType === FilterType.ComboBox) {
19629
- if (!Utilities.IsUndefined(field.OriginalData)) {
19630
- const fld = field.OriginalData.find((p) => p[field.ValueMember] == fv);
19631
- if (fld && fld.hasOwnProperty(field.DisplayMember)) displayedValue = `${fld[field.DisplayMember]}`;
19632
- }
19633
- } else if (field.FilterType === FilterType.String || field.FilterType === FilterType.Integer) displayedValue = `${fv}`;
19634
- else return null;
19635
- return displayedValue;
19880
+ get result() {
19881
+ return {
19882
+ filter: this.filter,
19883
+ sort: this.sort.filter((s) => !!s.property),
19884
+ saveInView: this.saveInView && this.canSaveInView
19885
+ };
19636
19886
  }
19637
- resetFormControl(field) {
19638
- if (!field.IsClearable) return;
19639
- const currentValue = this.currentCriteria != null ? this.currentCriteria[field.PropertyName] : this.wrapper.form.get(field.PropertyName)?.value;
19640
- if (Utilities.IsNil(currentValue) || currentValue === -1 || Utilities.isEmptyString(currentValue)) return;
19641
- let newValue;
19642
- if (field.FilterType === FilterType.ComboBox) newValue = field.Data?.find((d) => d[field.ValueMember] === -1) ? -1 : null;
19643
- else if (field.FilterType === FilterType.String) newValue = "";
19644
- else return;
19645
- this.wrapper.form.get(field.PropertyName).reset(newValue, { emitEvent: false });
19646
- this.facade.filter.setFilter(this.wrapper.form.getRawValue());
19887
+ columnOf(key) {
19888
+ return this.columns.find((c) => c.key === key);
19647
19889
  }
19648
- resetFieldData(field) {
19649
- const fv = this.wrapper.form.get(field.PropertyName).value;
19650
- field.Data = field.OriginalData.filter((p) => p[field.ValueMember] === -1 || Object.keys(field.CurrentFilters).every((name) => {
19651
- return p[name] === fv;
19652
- }));
19890
+ operatorsOf(condition) {
19891
+ return FilterOperators[this.columnOf(condition.Property)?.type ?? "text"];
19653
19892
  }
19654
- static {
19655
- this.ɵfac = i0.ɵɵngDeclareFactory({
19656
- minVersion: "12.0.0",
19657
- version: "22.2.1",
19658
- ngImport: i0,
19659
- type: FilterSummaryPanelComponent,
19660
- deps: [
19661
- { token: ValidationService },
19662
- { token: PropertyChangeBus },
19663
- { token: EntityFacadeRegistryService }
19664
- ],
19665
- target: i0.ɵɵFactoryTarget.Component
19666
- });
19893
+ hasValue(condition) {
19894
+ return !ViewFilterNoValueOperators.includes(condition.Operator);
19667
19895
  }
19668
- static {
19669
- this.ɵcmp = i0.ɵɵngDeclareComponent({
19670
- minVersion: "14.0.0",
19671
- version: "22.2.1",
19672
- type: FilterSummaryPanelComponent,
19673
- isStandalone: false,
19674
- selector: "alg-filter-summary-panel",
19675
- inputs: {
19676
- entityKey: "entityKey",
19677
- guid: "guid"
19678
- },
19679
- ngImport: i0,
19680
- template: "<!-- <div class=\"clearfix\" *ngIf=\"!isEmptyFilter()\">\r\n <div class=\"filter-summary\">\r\n <div class=\"filter\" [formGroup]=\"wrapper.form\">\r\n <ng-container *ngIf=\"filterList\">\r\n \r\n <div *ngFor=\"let field of Fields\" class=\"option\">\r\n <div [ngSwitch]=\"field.FilterType\">\r\n <div *ngSwitchCase=\"filterType.String\" class=\"form-group\">\r\n <input\r\n class=\"form-control\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n [readonly]=\"field.ReadOnly\"\r\n [type]=\"field.InputType\"\r\n />\r\n </div>\r\n <div\r\n *ngSwitchCase=\"filterType.Integer\"\r\n class=\"form-group\"\r\n >\r\n <div>\r\n <input\r\n class=\"form-control\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n type=\"number\"\r\n focusable\r\n ng-pattern=\"/^[0-9]+$/\"\r\n tabindex=\"0\"\r\n />\r\n </div>\r\n <div *ngSwitchCase=\"filterType.ComboBox\" class=\"form-group\">\r\n <div>\r\n <ng-select\r\n appendTo=\"body\"\r\n placeholder=\"Sélectionner {{\r\n field.Label\r\n }}\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n [items]=\"field.Data\"\r\n [multiple]=\"field.MultipleSelection\"\r\n [hideSelected]=\"field.HideSelected\"\r\n bindValue=\"{{ field.ValueMember }}\"\r\n [clearable]=\"field.IsClearable\"\r\n bindLabel=\"{{ field.DisplayMember }}\"\r\n [selectOnTab]=\"true\"\r\n [readonly]=\"field.ReadOnly\"\r\n >\r\n </ng-select>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</div> -->\r\n\r\n\r\n<div class=\"clearfix\" *ngIf=\"!isEmptyFilter()\">\r\n <div class=\"filter-summary\">\r\n <div class=\"filter\">\r\n <ng-container *ngIf=\"filterList\">\r\n <div *ngFor=\"let field of filterList\">\r\n <div class=\"option\" [class.clearable]=\"field.Field.IsClearable\" (click)=\"resetFormControl(field.Field)\">\r\n {{field.Field.Label + ' : '+ field.DisplayedValue}}\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</div>\r\n",
19681
- styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}.filter-summary{padding:2px 3px;border-bottom:solid 1px var(--main-bg-color);display:flex}.option alg-field .form-group{display:flex!important}.filter-summary .filter{display:flex;flex-direction:row;padding:2px}.filter-summary .filter .option{display:block;float:left;position:relative;background-color:#dbe4ff;text-transform:capitalize;color:#4465c9;padding:5px 10px;margin:0 5px 0 0;border-radius:20px;cursor:pointer;-webkit-transition:background-color .1s ease-out;-moz-transition:background-color .1s ease-out;-o-transition:background-color .1s ease-out;transition:background-color .1s ease-out;border-left:1px solid #ffffff}.filter-summary .filter .option.clearable:hover{text-decoration:line-through}.filter-summary .filter:before,.filter-summary .filter:after{content:\"\";display:table}.filter-summary:before,.filter-summary:after{content:\"\";display:table}\n"],
19682
- dependencies: [{
19683
- kind: "directive",
19684
- type: i1$5.NgForOf,
19685
- selector: "[ngFor][ngForOf]",
19686
- inputs: [
19687
- "ngForOf",
19688
- "ngForTrackBy",
19689
- "ngForTemplate"
19690
- ]
19691
- }, {
19692
- kind: "directive",
19693
- type: i1$5.NgIf,
19694
- selector: "[ngIf]",
19695
- inputs: [
19696
- "ngIf",
19697
- "ngIfThen",
19698
- "ngIfElse"
19699
- ]
19700
- }],
19701
- changeDetection: i0.ChangeDetectionStrategy.Eager
19702
- });
19896
+ hasSecondValue(condition) {
19897
+ return condition.Operator === "between" || condition.Operator === "datebetween";
19703
19898
  }
19704
- };
19705
- i0.ɵɵngDeclareClassMetadata({
19706
- minVersion: "12.0.0",
19707
- version: "22.2.1",
19708
- ngImport: i0,
19709
- type: FilterSummaryPanelComponent,
19710
- decorators: [{
19711
- type: Component,
19712
- args: [{
19713
- selector: "alg-filter-summary-panel",
19714
- changeDetection: ChangeDetectionStrategy.Eager,
19715
- standalone: false,
19716
- template: "<!-- <div class=\"clearfix\" *ngIf=\"!isEmptyFilter()\">\r\n <div class=\"filter-summary\">\r\n <div class=\"filter\" [formGroup]=\"wrapper.form\">\r\n <ng-container *ngIf=\"filterList\">\r\n \r\n <div *ngFor=\"let field of Fields\" class=\"option\">\r\n <div [ngSwitch]=\"field.FilterType\">\r\n <div *ngSwitchCase=\"filterType.String\" class=\"form-group\">\r\n <input\r\n class=\"form-control\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n [readonly]=\"field.ReadOnly\"\r\n [type]=\"field.InputType\"\r\n />\r\n </div>\r\n <div\r\n *ngSwitchCase=\"filterType.Integer\"\r\n class=\"form-group\"\r\n >\r\n <div>\r\n <input\r\n class=\"form-control\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n type=\"number\"\r\n focusable\r\n ng-pattern=\"/^[0-9]+$/\"\r\n tabindex=\"0\"\r\n />\r\n </div>\r\n <div *ngSwitchCase=\"filterType.ComboBox\" class=\"form-group\">\r\n <div>\r\n <ng-select\r\n appendTo=\"body\"\r\n placeholder=\"Sélectionner {{\r\n field.Label\r\n }}\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n [items]=\"field.Data\"\r\n [multiple]=\"field.MultipleSelection\"\r\n [hideSelected]=\"field.HideSelected\"\r\n bindValue=\"{{ field.ValueMember }}\"\r\n [clearable]=\"field.IsClearable\"\r\n bindLabel=\"{{ field.DisplayMember }}\"\r\n [selectOnTab]=\"true\"\r\n [readonly]=\"field.ReadOnly\"\r\n >\r\n </ng-select>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</div> -->\r\n\r\n\r\n<div class=\"clearfix\" *ngIf=\"!isEmptyFilter()\">\r\n <div class=\"filter-summary\">\r\n <div class=\"filter\">\r\n <ng-container *ngIf=\"filterList\">\r\n <div *ngFor=\"let field of filterList\">\r\n <div class=\"option\" [class.clearable]=\"field.Field.IsClearable\" (click)=\"resetFormControl(field.Field)\">\r\n {{field.Field.Label + ' : '+ field.DisplayedValue}}\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</div>\r\n",
19717
- styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}.filter-summary{padding:2px 3px;border-bottom:solid 1px var(--main-bg-color);display:flex}.option alg-field .form-group{display:flex!important}.filter-summary .filter{display:flex;flex-direction:row;padding:2px}.filter-summary .filter .option{display:block;float:left;position:relative;background-color:#dbe4ff;text-transform:capitalize;color:#4465c9;padding:5px 10px;margin:0 5px 0 0;border-radius:20px;cursor:pointer;-webkit-transition:background-color .1s ease-out;-moz-transition:background-color .1s ease-out;-o-transition:background-color .1s ease-out;transition:background-color .1s ease-out;border-left:1px solid #ffffff}.filter-summary .filter .option.clearable:hover{text-decoration:line-through}.filter-summary .filter:before,.filter-summary .filter:after{content:\"\";display:table}.filter-summary:before,.filter-summary:after{content:\"\";display:table}\n"]
19718
- }]
19719
- }],
19720
- ctorParameters: () => [
19721
- { type: ValidationService },
19722
- { type: PropertyChangeBus },
19723
- { type: EntityFacadeRegistryService }
19724
- ],
19725
- propDecorators: {
19726
- entityKey: [{ type: Input }],
19727
- guid: [{ type: Input }]
19899
+ isList(condition) {
19900
+ return condition.Operator === "in" || condition.Operator === "notin";
19728
19901
  }
19729
- });
19730
- var ViewModelLoaderService = class ViewModelLoaderService {
19731
- constructor(facades) {
19732
- this.facades = facades;
19902
+ inputType(condition) {
19903
+ const type = this.columnOf(condition.Property)?.type;
19904
+ return type === "date" ? "date" : type === "number" ? "number" : "text";
19733
19905
  }
19734
- getViewModel$(entityKey, guid, isStandalone = true, id) {
19735
- const facade = this.facades.getFacade(entityKey, guid);
19736
- const config$ = facade.config.viewModel$.pipe(filter((vm) => !!vm?.FieldSections?.length), shareReplay({
19737
- bufferSize: 1,
19738
- refCount: true
19739
- }));
19740
- if (isStandalone && (!id || id === "0")) return config$.pipe(map((cfg) => this.createNew(cfg)), shareReplay({
19741
- bufferSize: 1,
19742
- refCount: true
19743
- }));
19744
- if (isStandalone && id && id !== "0") {
19745
- facade.selection.getSelectedById$(id);
19746
- return facade.config.getApiViewModel(id);
19747
- }
19748
- return combineLatest([config$, facade.selection.selected$]).pipe(map(([cfg, selection]) => {
19749
- const adapted = facade.selection.adaptSelectionToViewModel(selection, cfg.FieldSections);
19750
- return {
19751
- ...cfg,
19752
- ViewModel: adapted ?? cfg.ViewModel,
19753
- FieldSections: cfg.FieldSections
19754
- };
19755
- }), shareReplay({
19756
- bufferSize: 1,
19757
- refCount: true
19758
- }));
19906
+ onColumnChange(condition) {
19907
+ condition.Operator = this.operatorsOf(condition)[0].value;
19908
+ condition.Value = null;
19909
+ condition.Value2 = null;
19910
+ condition.Values = this.isList(condition) ? [] : void 0;
19759
19911
  }
19760
- createNew(baseVm) {
19761
- const clone = {
19762
- ...baseVm.ViewModel,
19763
- Id: null
19912
+ onOperatorChange(condition) {
19913
+ if (this.isList(condition)) condition.Values = condition.Values ?? [];
19914
+ }
19915
+ toggleValue(condition, value) {
19916
+ const values = condition.Values ?? [];
19917
+ condition.Values = values.includes(value) ? values.filter((v) => v !== value) : [...values, value];
19918
+ }
19919
+ setLogic(group, logic) {
19920
+ group.Logic = logic;
19921
+ }
19922
+ addCondition(group) {
19923
+ const column = this.columns[0];
19924
+ if (!column) return;
19925
+ const condition = {
19926
+ Property: column.key,
19927
+ Operator: FilterOperators[column.type][0].value,
19928
+ Value: null
19764
19929
  };
19765
- return {
19766
- ...baseVm,
19767
- ViewModel: clone,
19768
- FieldSections: baseVm.FieldSections
19930
+ if (this.isList(condition)) condition.Values = [];
19931
+ group.Conditions.push(condition);
19932
+ }
19933
+ removeCondition(group, index) {
19934
+ group.Conditions.splice(index, 1);
19935
+ }
19936
+ addGroup(group) {
19937
+ const sub = {
19938
+ Logic: group.Logic === "and" ? "or" : "and",
19939
+ Conditions: [],
19940
+ Groups: []
19769
19941
  };
19942
+ this.addCondition(sub);
19943
+ group.Groups = [...group.Groups ?? [], sub];
19944
+ }
19945
+ removeGroup(parent, index) {
19946
+ parent.Groups?.splice(index, 1);
19947
+ }
19948
+ clear() {
19949
+ delete this.filter.Panel;
19950
+ this.addedPanelKeys = [];
19951
+ this.filter.Logic = "and";
19952
+ this.filter.Conditions.splice(0);
19953
+ this.filter.Groups = [];
19954
+ this.sort.splice(0);
19955
+ }
19956
+ addSort() {
19957
+ const used = new Set(this.sort.map((s) => s.property));
19958
+ const next = this.columns.find((c) => !used.has(c.key));
19959
+ if (next) this.sort.push({
19960
+ property: next.key,
19961
+ direction: "asc"
19962
+ });
19963
+ }
19964
+ removeSort(index) {
19965
+ this.sort.splice(index, 1);
19966
+ }
19967
+ moveSort(index, offset) {
19968
+ const target = index + offset;
19969
+ if (target < 0 || target >= this.sort.length) return;
19970
+ const [level] = this.sort.splice(index, 1);
19971
+ this.sort.splice(target, 0, level);
19972
+ }
19973
+ summary(group = this.filter) {
19974
+ return [...group.Conditions.map((c) => {
19975
+ const column = this.columnOf(c.Property);
19976
+ const operator = FilterOperators[column?.type ?? "text"].find((o) => o.value === c.Operator)?.label ?? c.Operator;
19977
+ const values = this.isList(c) ? (c.Values ?? []).map((v) => column?.values?.find((x) => x.value === v)?.label ?? v).join(", ") : !this.hasValue(c) ? "" : this.hasSecondValue(c) ? `${c.Value ?? "…"} et ${c.Value2 ?? "…"}` : c.Value ?? "…";
19978
+ return `${column?.label ?? c.Property} ${operator}${values ? " « " + values + " »" : ""}`;
19979
+ }), ...(group.Groups ?? []).map((g) => `(${this.summary(g)})`)].join(group.Logic === "or" ? " OU " : " ET ") || "Aucun filtre";
19770
19980
  }
19771
19981
  static {
19772
19982
  this.ɵfac = i0.ɵɵngDeclareFactory({
19773
19983
  minVersion: "12.0.0",
19774
19984
  version: "22.2.1",
19775
19985
  ngImport: i0,
19776
- type: ViewModelLoaderService,
19777
- deps: [{ token: EntityFacadeRegistryService }],
19778
- target: i0.ɵɵFactoryTarget.Injectable
19986
+ type: FilterEditorComponent,
19987
+ deps: [],
19988
+ target: i0.ɵɵFactoryTarget.Component
19779
19989
  });
19780
19990
  }
19781
19991
  static {
19782
- this.ɵprov = i0.ɵɵngDeclareInjectable({
19783
- minVersion: "12.0.0",
19992
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
19993
+ minVersion: "14.0.0",
19784
19994
  version: "22.2.1",
19995
+ type: FilterEditorComponent,
19996
+ isStandalone: true,
19997
+ selector: "alg-filter-editor",
19998
+ inputs: {
19999
+ columns: "columns",
20000
+ filter: "filter",
20001
+ sort: "sort",
20002
+ embedded: "embedded",
20003
+ section: "section",
20004
+ panelFields: "panelFields"
20005
+ },
19785
20006
  ngImport: i0,
19786
- type: ViewModelLoaderService
19787
- });
19788
- }
19789
- };
19790
- i0.ɵɵngDeclareClassMetadata({
19791
- minVersion: "12.0.0",
19792
- version: "22.2.1",
19793
- ngImport: i0,
19794
- type: ViewModelLoaderService,
19795
- decorators: [{ type: Injectable }],
19796
- ctorParameters: () => [{ type: EntityFacadeRegistryService }]
19797
- });
19798
- var PropertyEditorComponent = class PropertyEditorComponent {
19799
- handleKeywordsChanged(event) {}
19800
- isFieldHidden(field) {
19801
- return !(Utilities.IsNil(this.keyword) || field.Label.toUpperCase().includes(this.keyword.toUpperCase()));
20007
+ template: "<div class=\"fe\">\r\n <div class=\"fe-tabs\" role=\"tablist\" *ngIf=\"!section\">\r\n <button type=\"button\" class=\"fe-tab\" [class.active]=\"tab === 'filters'\" (click)=\"tab = 'filters'\">Filtres</button>\r\n <button type=\"button\" class=\"fe-tab\" [class.active]=\"tab === 'sort'\" (click)=\"tab = 'sort'\">Tri</button>\r\n </div>\r\n\r\n <div class=\"fe-body\" *ngIf=\"shownTab === 'filters'\">\r\n <!-- Filtres de l'écran (FilterPanel) : valeurs enregistrées avec la vue, appliquées au FilterPanel. -->\r\n <div class=\"fe-panel\" *ngIf=\"panelFields.length || shownPanelFields().length\">\r\n <div class=\"fe-panel-title\">Filtres de l'écran</div>\r\n <div class=\"fe-panel-field\" *ngFor=\"let field of shownPanelFields(); trackBy: trackKey\">\r\n <span [title]=\"field.label\">{{ field.label }}</span>\r\n <ng-container [ngSwitch]=\"field.type\">\r\n <select *ngSwitchCase=\"'list'\" class=\"form-control\" [ngModel]=\"panelValue(field)\" (ngModelChange)=\"setPanelValue(field, $event)\">\r\n <option value=\"\">Choisir…</option>\r\n <option *ngFor=\"let option of field.values\" [value]=\"option.value\">{{ option.label }}</option>\r\n </select>\r\n <select *ngSwitchCase=\"'bool'\" class=\"form-control\" [ngModel]=\"panelValue(field)\" (ngModelChange)=\"setPanelValue(field, $event)\">\r\n <option value=\"\">Choisir…</option>\r\n <option value=\"true\">Oui</option>\r\n <option value=\"false\">Non</option>\r\n </select>\r\n <input *ngSwitchDefault class=\"form-control\" [type]=\"field.type === 'number' ? 'number' : field.type === 'date' ? 'date' : 'text'\"\r\n [ngModel]=\"panelValue(field)\" (ngModelChange)=\"setPanelValue(field, $event)\" />\r\n </ng-container>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer le filtre de l'écran\" (click)=\"removePanelField(field)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n <!-- Ajout d'un filtre de l'écran : les champs du FilterPanel pas encore utilisés. -->\r\n <div class=\"fe-adds\" *ngIf=\"availablePanelFields().length\">\r\n <select class=\"form-control fe-add-panel\" [ngModel]=\"''\" (ngModelChange)=\"addPanelField($event)\">\r\n <option value=\"\">+ Filtre de l'écran</option>\r\n <option *ngFor=\"let field of availablePanelFields(); trackBy: trackKey\" [value]=\"field.key\">{{ field.label }}</option>\r\n </select>\r\n </div>\r\n </div>\r\n <div class=\"fe-panel-title\" *ngIf=\"panelFields.length || shownPanelFields().length\">Filtre additionnel</div>\r\n <ng-container *ngTemplateOutlet=\"groupTpl; context: { group: filter, parent: null, index: 0, depth: 0 }\"></ng-container>\r\n <div class=\"fe-summary\">{{ summary() }}</div>\r\n </div>\r\n\r\n <div class=\"fe-body\" *ngIf=\"shownTab === 'sort'\">\r\n <div class=\"fe-sort\" *ngFor=\"let level of sort; let i = index; trackBy: trackIndex\">\r\n <span class=\"fe-sort-label\">{{ i === 0 ? 'Trier par' : 'Puis par' }}</span>\r\n <select class=\"form-control\" [(ngModel)]=\"level.property\">\r\n <option *ngFor=\"let column of columns\" [value]=\"column.key\">{{ column.label }}</option>\r\n </select>\r\n <select class=\"form-control fe-sort-direction\" [(ngModel)]=\"level.direction\">\r\n <option value=\"asc\">Croissant</option>\r\n <option value=\"desc\">Décroissant</option>\r\n </select>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Monter\" [disabled]=\"i === 0\" (click)=\"moveSort(i, -1)\"><i class=\"fa fa-arrow-up\"></i></button>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Descendre\" [disabled]=\"i === sort.length - 1\" (click)=\"moveSort(i, 1)\"><i class=\"fa fa-arrow-down\"></i></button>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer\" (click)=\"removeSort(i)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n <div class=\"fe-adds\"><button type=\"button\" class=\"fe-add\" (click)=\"addSort()\">+ Niveau</button></div>\r\n </div>\r\n\r\n <div class=\"fe-foot\">\r\n <label class=\"fe-save\" *ngIf=\"!embedded\" [class.disabled]=\"!canSaveInView\"\r\n [title]=\"canSaveInView ? '' : 'La vue par défaut est en lecture seule : dupliquez-la pour y enregistrer un filtre'\">\r\n <input type=\"checkbox\" [(ngModel)]=\"saveInView\" [disabled]=\"!canSaveInView\" />\r\n <span>Enregistrer dans la vue{{ viewLabel ? ' « ' + viewLabel + ' »' : '' }}</span>\r\n </label>\r\n <button type=\"button\" class=\"fe-clear\" (click)=\"clear()\">Tout effacer</button>\r\n </div>\r\n</div>\r\n\r\n<!-- Groupe : relation ET / OU, conditions, groupes imbriqués. -->\r\n<ng-template #groupTpl let-group=\"group\" let-parent=\"parent\" let-index=\"index\" let-depth=\"depth\">\r\n <div class=\"fe-group\" [class.or]=\"group.Logic === 'or'\" [class.nested]=\"depth > 0\">\r\n <div class=\"fe-group-head\">\r\n <span class=\"fe-logic\">\r\n <button type=\"button\" [class.on]=\"group.Logic === 'and'\" class=\"and\" (click)=\"setLogic(group, 'and')\">ET</button>\r\n <button type=\"button\" [class.on]=\"group.Logic === 'or'\" class=\"or\" (click)=\"setLogic(group, 'or')\">OU</button>\r\n </span>\r\n <span class=\"fe-group-hint\">{{ group.Logic === 'and' ? 'Toutes les conditions' : 'Au moins une condition' }}</span>\r\n <button type=\"button\" *ngIf=\"parent\" class=\"fe-icon fe-group-remove\" title=\"Retirer le groupe\" (click)=\"removeGroup(parent, index)\">\r\n <i class=\"fa fa-times\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"fe-cond\" *ngFor=\"let condition of group.Conditions; let i = index; trackBy: trackIndex\">\r\n <select class=\"form-control\" [(ngModel)]=\"condition.Property\" (ngModelChange)=\"onColumnChange(condition)\">\r\n <option *ngFor=\"let column of columns\" [value]=\"column.key\">{{ column.label }}</option>\r\n </select>\r\n <select class=\"form-control\" [(ngModel)]=\"condition.Operator\" (ngModelChange)=\"onOperatorChange(condition)\">\r\n <option *ngFor=\"let op of operatorsOf(condition)\" [value]=\"op.value\">{{ op.label }}</option>\r\n </select>\r\n <span class=\"fe-value\">\r\n <ng-container *ngIf=\"isList(condition); else valueTpl\">\r\n <span class=\"fe-chips\">\r\n <button type=\"button\" *ngFor=\"let option of columnOf(condition.Property)?.values\" class=\"fe-chip\"\r\n [class.on]=\"condition.Values?.includes(option.value)\" (click)=\"toggleValue(condition, option.value)\">{{ option.label }}</button>\r\n </span>\r\n </ng-container>\r\n <ng-template #valueTpl>\r\n <ng-container *ngIf=\"hasValue(condition)\">\r\n <input class=\"form-control\" [type]=\"inputType(condition)\" [(ngModel)]=\"condition.Value\" />\r\n <input *ngIf=\"hasSecondValue(condition)\" class=\"form-control\" [type]=\"inputType(condition)\" [(ngModel)]=\"condition.Value2\" />\r\n </ng-container>\r\n </ng-template>\r\n </span>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer la condition\" (click)=\"removeCondition(group, i)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n\r\n <ng-container *ngFor=\"let sub of group.Groups; let gi = index; trackBy: trackIndex\">\r\n <ng-container *ngTemplateOutlet=\"groupTpl; context: { group: sub, parent: group, index: gi, depth: depth + 1 }\"></ng-container>\r\n </ng-container>\r\n\r\n <div class=\"fe-adds\">\r\n <button type=\"button\" class=\"fe-add\" (click)=\"addCondition(group)\">+ Condition</button>\r\n <button type=\"button\" class=\"fe-add\" *ngIf=\"depth < maxDepth - 1\" (click)=\"addGroup(group)\">+ Groupe</button>\r\n </div>\r\n </div>\r\n</ng-template>\r\n",
20008
+ styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}.fe-panel{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(260px,100%),1fr));gap:6px 12px;margin-bottom:12px}.fe-panel .fe-adds{grid-column:1/-1}.fe-add-panel{width:auto;border-style:dashed;color:#4465c9}.fe-panel-title{grid-column:1/-1;margin:4px 0 2px;font-weight:600}.fe-panel-field{display:grid;grid-template-columns:120px minmax(0,1fr) auto;align-items:center;gap:8px;margin:0;font-weight:400}.fe-panel-field>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fe{display:flex;flex-direction:column;color:#4d5a81;font-size:13px}.fe-tabs{display:flex;gap:4px;padding:8px 14px 0;border-bottom:1px solid #DBE4FF}.fe-tab{padding:6px 12px;border:none;border-bottom:2px solid transparent;background:none;color:#9fa9c7;font:inherit;font-weight:700;cursor:pointer}.fe-tab.active{color:#4465c9;border-bottom-color:#5272d3}.fe-body{display:flex;flex-direction:column;gap:10px;max-height:60vh;overflow-y:auto;padding:12px 14px}.fe-group{display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid #DBE4FF;border-left:4px solid #3b57b8;border-radius:0 4px 4px 0;background:#fff}.fe-group.or{border-left-color:#e07b12}.fe-group.nested{background:#f6f8fd}.fe-group-head{display:flex;align-items:center;gap:8px;font-size:12px;color:#9fa9c7}.fe-group-remove{margin-left:auto}.fe-logic{display:inline-flex;border:1px solid #DBE4FF;border-radius:999px;overflow:hidden}.fe-logic button{padding:2px 10px;border:none;background:none;color:#9fa9c7;font:inherit;font-size:11px;font-weight:800;cursor:pointer}.fe-logic button.on.and{background:#3b57b8;color:#fff}.fe-logic button.on.or{background:#e07b12;color:#fff}.fe-cond{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) minmax(0,1.4fr) 28px;gap:6px;align-items:center;padding:4px 6px;border:1px solid #DBE4FF;border-left:3px solid #3b57b8;border-radius:0 4px 4px 0;background:#fff}.fe-group.or>.fe-cond{border-left-color:#e07b12}@media(max-width:600px){.fe-cond{grid-template-columns:minmax(0,1fr) minmax(0,1fr) 28px}.fe-cond .fe-value{grid-column:1/3}}select.form-control,input.form-control{height:30px;min-width:0;padding:2px 6px;font-size:13px}.fe-value{display:flex;gap:6px;min-width:0}.fe-value input{flex:1}.fe-chips{display:flex;flex-wrap:wrap;gap:4px}.fe-chip{padding:2px 8px;border:1px solid #C1D0FD;border-radius:999px;background:#fff;color:#4d5a81;font:inherit;font-size:12px;cursor:pointer}.fe-chip.on{background:#5272d3;border-color:#5272d3;color:#fff}.fe-icon{width:28px;height:28px;padding:0;border:none;border-radius:6px;background:none;color:#9fa9c7;cursor:pointer}.fe-icon:hover:not(:disabled){background:#ebf0ff;color:#4d5a81}.fe-icon:disabled{opacity:.3;cursor:default}.fe-adds{display:flex;gap:8px;flex-wrap:wrap}.fe-add{padding:3px 10px;border:1px dashed #C1D0FD;border-radius:6px;background:none;color:#4465c9;font:inherit;font-size:12px;cursor:pointer}.fe-summary{padding:8px 10px;border-radius:6px;background:#ebf0ff;font-size:12px;word-break:break-word}.fe-sort{display:grid;grid-template-columns:64px minmax(0,1fr) 120px 28px 28px 28px;gap:6px;align-items:center}.fe-sort-label{font-size:12px;color:#9fa9c7}.fe-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 14px;border-top:1px solid #DBE4FF}.fe-save{display:inline-flex;align-items:center;gap:6px;margin:0;font-weight:400;cursor:pointer}.fe-save.disabled{opacity:.55;cursor:default}.fe-save input{margin:0}.fe-clear{border:none;background:none;color:#4465c9;font:inherit;cursor:pointer}\n"],
20009
+ dependencies: [
20010
+ {
20011
+ kind: "ngmodule",
20012
+ type: CommonModule
20013
+ },
20014
+ {
20015
+ kind: "directive",
20016
+ type: i1$5.NgForOf,
20017
+ selector: "[ngFor][ngForOf]",
20018
+ inputs: [
20019
+ "ngForOf",
20020
+ "ngForTrackBy",
20021
+ "ngForTemplate"
20022
+ ]
20023
+ },
20024
+ {
20025
+ kind: "directive",
20026
+ type: i1$5.NgIf,
20027
+ selector: "[ngIf]",
20028
+ inputs: [
20029
+ "ngIf",
20030
+ "ngIfThen",
20031
+ "ngIfElse"
20032
+ ]
20033
+ },
20034
+ {
20035
+ kind: "directive",
20036
+ type: i1$5.NgTemplateOutlet,
20037
+ selector: "[ngTemplateOutlet]",
20038
+ inputs: [
20039
+ "ngTemplateOutletContext",
20040
+ "ngTemplateOutlet",
20041
+ "ngTemplateOutletInjector"
20042
+ ]
20043
+ },
20044
+ {
20045
+ kind: "directive",
20046
+ type: i1$5.NgSwitch,
20047
+ selector: "[ngSwitch]",
20048
+ inputs: ["ngSwitch"]
20049
+ },
20050
+ {
20051
+ kind: "directive",
20052
+ type: i1$5.NgSwitchCase,
20053
+ selector: "[ngSwitchCase]",
20054
+ inputs: ["ngSwitchCase"]
20055
+ },
20056
+ {
20057
+ kind: "directive",
20058
+ type: i1$5.NgSwitchDefault,
20059
+ selector: "[ngSwitchDefault]"
20060
+ },
20061
+ {
20062
+ kind: "ngmodule",
20063
+ type: FormsModule
20064
+ },
20065
+ {
20066
+ kind: "directive",
20067
+ type: i5.NgSelectOption,
20068
+ selector: "option",
20069
+ inputs: ["ngValue", "value"]
20070
+ },
20071
+ {
20072
+ kind: "directive",
20073
+ type: i5.ɵNgSelectMultipleOption,
20074
+ selector: "option",
20075
+ inputs: ["ngValue", "value"]
20076
+ },
20077
+ {
20078
+ kind: "directive",
20079
+ type: i5.DefaultValueAccessor,
20080
+ selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"
20081
+ },
20082
+ {
20083
+ kind: "directive",
20084
+ type: i5.CheckboxControlValueAccessor,
20085
+ selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]"
20086
+ },
20087
+ {
20088
+ kind: "directive",
20089
+ type: i5.SelectControlValueAccessor,
20090
+ selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]",
20091
+ inputs: ["compareWith"]
20092
+ },
20093
+ {
20094
+ kind: "directive",
20095
+ type: i5.NgControlStatus,
20096
+ selector: "[formControlName],[ngModel],[formControl]"
20097
+ },
20098
+ {
20099
+ kind: "directive",
20100
+ type: i5.NgModel,
20101
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
20102
+ inputs: [
20103
+ "name",
20104
+ "disabled",
20105
+ "ngModel",
20106
+ "ngModelOptions"
20107
+ ],
20108
+ outputs: ["ngModelChange"],
20109
+ exportAs: ["ngModel"]
20110
+ }
20111
+ ],
20112
+ changeDetection: i0.ChangeDetectionStrategy.Eager
20113
+ });
19802
20114
  }
19803
- isSectionHidden(section) {
19804
- return !(Utilities.IsNil(this.keyword) || section.Name.toUpperCase().includes(this.keyword.toUpperCase()) || section.FieldViewModels.some((f) => !this.isFieldHidden(f)));
20115
+ };
20116
+ i0.ɵɵngDeclareClassMetadata({
20117
+ minVersion: "12.0.0",
20118
+ version: "22.2.1",
20119
+ ngImport: i0,
20120
+ type: FilterEditorComponent,
20121
+ decorators: [{
20122
+ type: Component,
20123
+ args: [{
20124
+ selector: "alg-filter-editor",
20125
+ standalone: true,
20126
+ imports: [CommonModule, FormsModule],
20127
+ changeDetection: ChangeDetectionStrategy.Eager,
20128
+ template: "<div class=\"fe\">\r\n <div class=\"fe-tabs\" role=\"tablist\" *ngIf=\"!section\">\r\n <button type=\"button\" class=\"fe-tab\" [class.active]=\"tab === 'filters'\" (click)=\"tab = 'filters'\">Filtres</button>\r\n <button type=\"button\" class=\"fe-tab\" [class.active]=\"tab === 'sort'\" (click)=\"tab = 'sort'\">Tri</button>\r\n </div>\r\n\r\n <div class=\"fe-body\" *ngIf=\"shownTab === 'filters'\">\r\n <!-- Filtres de l'écran (FilterPanel) : valeurs enregistrées avec la vue, appliquées au FilterPanel. -->\r\n <div class=\"fe-panel\" *ngIf=\"panelFields.length || shownPanelFields().length\">\r\n <div class=\"fe-panel-title\">Filtres de l'écran</div>\r\n <div class=\"fe-panel-field\" *ngFor=\"let field of shownPanelFields(); trackBy: trackKey\">\r\n <span [title]=\"field.label\">{{ field.label }}</span>\r\n <ng-container [ngSwitch]=\"field.type\">\r\n <select *ngSwitchCase=\"'list'\" class=\"form-control\" [ngModel]=\"panelValue(field)\" (ngModelChange)=\"setPanelValue(field, $event)\">\r\n <option value=\"\">Choisir…</option>\r\n <option *ngFor=\"let option of field.values\" [value]=\"option.value\">{{ option.label }}</option>\r\n </select>\r\n <select *ngSwitchCase=\"'bool'\" class=\"form-control\" [ngModel]=\"panelValue(field)\" (ngModelChange)=\"setPanelValue(field, $event)\">\r\n <option value=\"\">Choisir…</option>\r\n <option value=\"true\">Oui</option>\r\n <option value=\"false\">Non</option>\r\n </select>\r\n <input *ngSwitchDefault class=\"form-control\" [type]=\"field.type === 'number' ? 'number' : field.type === 'date' ? 'date' : 'text'\"\r\n [ngModel]=\"panelValue(field)\" (ngModelChange)=\"setPanelValue(field, $event)\" />\r\n </ng-container>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer le filtre de l'écran\" (click)=\"removePanelField(field)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n <!-- Ajout d'un filtre de l'écran : les champs du FilterPanel pas encore utilisés. -->\r\n <div class=\"fe-adds\" *ngIf=\"availablePanelFields().length\">\r\n <select class=\"form-control fe-add-panel\" [ngModel]=\"''\" (ngModelChange)=\"addPanelField($event)\">\r\n <option value=\"\">+ Filtre de l'écran</option>\r\n <option *ngFor=\"let field of availablePanelFields(); trackBy: trackKey\" [value]=\"field.key\">{{ field.label }}</option>\r\n </select>\r\n </div>\r\n </div>\r\n <div class=\"fe-panel-title\" *ngIf=\"panelFields.length || shownPanelFields().length\">Filtre additionnel</div>\r\n <ng-container *ngTemplateOutlet=\"groupTpl; context: { group: filter, parent: null, index: 0, depth: 0 }\"></ng-container>\r\n <div class=\"fe-summary\">{{ summary() }}</div>\r\n </div>\r\n\r\n <div class=\"fe-body\" *ngIf=\"shownTab === 'sort'\">\r\n <div class=\"fe-sort\" *ngFor=\"let level of sort; let i = index; trackBy: trackIndex\">\r\n <span class=\"fe-sort-label\">{{ i === 0 ? 'Trier par' : 'Puis par' }}</span>\r\n <select class=\"form-control\" [(ngModel)]=\"level.property\">\r\n <option *ngFor=\"let column of columns\" [value]=\"column.key\">{{ column.label }}</option>\r\n </select>\r\n <select class=\"form-control fe-sort-direction\" [(ngModel)]=\"level.direction\">\r\n <option value=\"asc\">Croissant</option>\r\n <option value=\"desc\">Décroissant</option>\r\n </select>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Monter\" [disabled]=\"i === 0\" (click)=\"moveSort(i, -1)\"><i class=\"fa fa-arrow-up\"></i></button>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Descendre\" [disabled]=\"i === sort.length - 1\" (click)=\"moveSort(i, 1)\"><i class=\"fa fa-arrow-down\"></i></button>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer\" (click)=\"removeSort(i)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n <div class=\"fe-adds\"><button type=\"button\" class=\"fe-add\" (click)=\"addSort()\">+ Niveau</button></div>\r\n </div>\r\n\r\n <div class=\"fe-foot\">\r\n <label class=\"fe-save\" *ngIf=\"!embedded\" [class.disabled]=\"!canSaveInView\"\r\n [title]=\"canSaveInView ? '' : 'La vue par défaut est en lecture seule : dupliquez-la pour y enregistrer un filtre'\">\r\n <input type=\"checkbox\" [(ngModel)]=\"saveInView\" [disabled]=\"!canSaveInView\" />\r\n <span>Enregistrer dans la vue{{ viewLabel ? ' « ' + viewLabel + ' »' : '' }}</span>\r\n </label>\r\n <button type=\"button\" class=\"fe-clear\" (click)=\"clear()\">Tout effacer</button>\r\n </div>\r\n</div>\r\n\r\n<!-- Groupe : relation ET / OU, conditions, groupes imbriqués. -->\r\n<ng-template #groupTpl let-group=\"group\" let-parent=\"parent\" let-index=\"index\" let-depth=\"depth\">\r\n <div class=\"fe-group\" [class.or]=\"group.Logic === 'or'\" [class.nested]=\"depth > 0\">\r\n <div class=\"fe-group-head\">\r\n <span class=\"fe-logic\">\r\n <button type=\"button\" [class.on]=\"group.Logic === 'and'\" class=\"and\" (click)=\"setLogic(group, 'and')\">ET</button>\r\n <button type=\"button\" [class.on]=\"group.Logic === 'or'\" class=\"or\" (click)=\"setLogic(group, 'or')\">OU</button>\r\n </span>\r\n <span class=\"fe-group-hint\">{{ group.Logic === 'and' ? 'Toutes les conditions' : 'Au moins une condition' }}</span>\r\n <button type=\"button\" *ngIf=\"parent\" class=\"fe-icon fe-group-remove\" title=\"Retirer le groupe\" (click)=\"removeGroup(parent, index)\">\r\n <i class=\"fa fa-times\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"fe-cond\" *ngFor=\"let condition of group.Conditions; let i = index; trackBy: trackIndex\">\r\n <select class=\"form-control\" [(ngModel)]=\"condition.Property\" (ngModelChange)=\"onColumnChange(condition)\">\r\n <option *ngFor=\"let column of columns\" [value]=\"column.key\">{{ column.label }}</option>\r\n </select>\r\n <select class=\"form-control\" [(ngModel)]=\"condition.Operator\" (ngModelChange)=\"onOperatorChange(condition)\">\r\n <option *ngFor=\"let op of operatorsOf(condition)\" [value]=\"op.value\">{{ op.label }}</option>\r\n </select>\r\n <span class=\"fe-value\">\r\n <ng-container *ngIf=\"isList(condition); else valueTpl\">\r\n <span class=\"fe-chips\">\r\n <button type=\"button\" *ngFor=\"let option of columnOf(condition.Property)?.values\" class=\"fe-chip\"\r\n [class.on]=\"condition.Values?.includes(option.value)\" (click)=\"toggleValue(condition, option.value)\">{{ option.label }}</button>\r\n </span>\r\n </ng-container>\r\n <ng-template #valueTpl>\r\n <ng-container *ngIf=\"hasValue(condition)\">\r\n <input class=\"form-control\" [type]=\"inputType(condition)\" [(ngModel)]=\"condition.Value\" />\r\n <input *ngIf=\"hasSecondValue(condition)\" class=\"form-control\" [type]=\"inputType(condition)\" [(ngModel)]=\"condition.Value2\" />\r\n </ng-container>\r\n </ng-template>\r\n </span>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer la condition\" (click)=\"removeCondition(group, i)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n\r\n <ng-container *ngFor=\"let sub of group.Groups; let gi = index; trackBy: trackIndex\">\r\n <ng-container *ngTemplateOutlet=\"groupTpl; context: { group: sub, parent: group, index: gi, depth: depth + 1 }\"></ng-container>\r\n </ng-container>\r\n\r\n <div class=\"fe-adds\">\r\n <button type=\"button\" class=\"fe-add\" (click)=\"addCondition(group)\">+ Condition</button>\r\n <button type=\"button\" class=\"fe-add\" *ngIf=\"depth < maxDepth - 1\" (click)=\"addGroup(group)\">+ Groupe</button>\r\n </div>\r\n </div>\r\n</ng-template>\r\n",
20129
+ styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}.fe-panel{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(260px,100%),1fr));gap:6px 12px;margin-bottom:12px}.fe-panel .fe-adds{grid-column:1/-1}.fe-add-panel{width:auto;border-style:dashed;color:#4465c9}.fe-panel-title{grid-column:1/-1;margin:4px 0 2px;font-weight:600}.fe-panel-field{display:grid;grid-template-columns:120px minmax(0,1fr) auto;align-items:center;gap:8px;margin:0;font-weight:400}.fe-panel-field>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fe{display:flex;flex-direction:column;color:#4d5a81;font-size:13px}.fe-tabs{display:flex;gap:4px;padding:8px 14px 0;border-bottom:1px solid #DBE4FF}.fe-tab{padding:6px 12px;border:none;border-bottom:2px solid transparent;background:none;color:#9fa9c7;font:inherit;font-weight:700;cursor:pointer}.fe-tab.active{color:#4465c9;border-bottom-color:#5272d3}.fe-body{display:flex;flex-direction:column;gap:10px;max-height:60vh;overflow-y:auto;padding:12px 14px}.fe-group{display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid #DBE4FF;border-left:4px solid #3b57b8;border-radius:0 4px 4px 0;background:#fff}.fe-group.or{border-left-color:#e07b12}.fe-group.nested{background:#f6f8fd}.fe-group-head{display:flex;align-items:center;gap:8px;font-size:12px;color:#9fa9c7}.fe-group-remove{margin-left:auto}.fe-logic{display:inline-flex;border:1px solid #DBE4FF;border-radius:999px;overflow:hidden}.fe-logic button{padding:2px 10px;border:none;background:none;color:#9fa9c7;font:inherit;font-size:11px;font-weight:800;cursor:pointer}.fe-logic button.on.and{background:#3b57b8;color:#fff}.fe-logic button.on.or{background:#e07b12;color:#fff}.fe-cond{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) minmax(0,1.4fr) 28px;gap:6px;align-items:center;padding:4px 6px;border:1px solid #DBE4FF;border-left:3px solid #3b57b8;border-radius:0 4px 4px 0;background:#fff}.fe-group.or>.fe-cond{border-left-color:#e07b12}@media(max-width:600px){.fe-cond{grid-template-columns:minmax(0,1fr) minmax(0,1fr) 28px}.fe-cond .fe-value{grid-column:1/3}}select.form-control,input.form-control{height:30px;min-width:0;padding:2px 6px;font-size:13px}.fe-value{display:flex;gap:6px;min-width:0}.fe-value input{flex:1}.fe-chips{display:flex;flex-wrap:wrap;gap:4px}.fe-chip{padding:2px 8px;border:1px solid #C1D0FD;border-radius:999px;background:#fff;color:#4d5a81;font:inherit;font-size:12px;cursor:pointer}.fe-chip.on{background:#5272d3;border-color:#5272d3;color:#fff}.fe-icon{width:28px;height:28px;padding:0;border:none;border-radius:6px;background:none;color:#9fa9c7;cursor:pointer}.fe-icon:hover:not(:disabled){background:#ebf0ff;color:#4d5a81}.fe-icon:disabled{opacity:.3;cursor:default}.fe-adds{display:flex;gap:8px;flex-wrap:wrap}.fe-add{padding:3px 10px;border:1px dashed #C1D0FD;border-radius:6px;background:none;color:#4465c9;font:inherit;font-size:12px;cursor:pointer}.fe-summary{padding:8px 10px;border-radius:6px;background:#ebf0ff;font-size:12px;word-break:break-word}.fe-sort{display:grid;grid-template-columns:64px minmax(0,1fr) 120px 28px 28px 28px;gap:6px;align-items:center}.fe-sort-label{font-size:12px;color:#9fa9c7}.fe-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 14px;border-top:1px solid #DBE4FF}.fe-save{display:inline-flex;align-items:center;gap:6px;margin:0;font-weight:400;cursor:pointer}.fe-save.disabled{opacity:.55;cursor:default}.fe-save input{margin:0}.fe-clear{border:none;background:none;color:#4465c9;font:inherit;cursor:pointer}\n"]
20130
+ }]
20131
+ }],
20132
+ propDecorators: {
20133
+ columns: [{ type: Input }],
20134
+ filter: [{ type: Input }],
20135
+ sort: [{ type: Input }],
20136
+ embedded: [{ type: Input }],
20137
+ section: [{ type: Input }],
20138
+ panelFields: [{ type: Input }]
19805
20139
  }
19806
- constructor(elementRef, toastrService, validationService, vmLoader, entityFacadeRegistryService, route, bus) {
19807
- this.elementRef = elementRef;
19808
- this.toastrService = toastrService;
20140
+ });
20141
+ var FilterSummaryPanelComponent = class FilterSummaryPanelComponent {
20142
+ isEmptyFilter() {
20143
+ return this.filterList.length == 0 && !this.viewFilterText;
20144
+ }
20145
+ editViewFilter() {
20146
+ this.facade.filter.editViewFilter();
20147
+ }
20148
+ saveViewFilter() {
20149
+ this.facade.filter.viewFilterHandler?.save();
20150
+ }
20151
+ cancelViewFilter() {
20152
+ this.facade.filter.viewFilterHandler?.cancel();
20153
+ }
20154
+ constructor(validationService, bus, entityFacadeRegistryService) {
19809
20155
  this.validationService = validationService;
19810
- this.vmLoader = vmLoader;
19811
- this.entityFacadeRegistryService = entityFacadeRegistryService;
19812
- this.route = route;
19813
20156
  this.bus = bus;
19814
- this.validateAfterEachChange = true;
19815
- this.searchText = "Rechercher une propriété...";
19816
- this.readonly = false;
19817
- this.fileChange$ = new Subject();
19818
- this.selectorChange$ = new Subject();
19819
- this.buttonClick$ = new Subject();
19820
- this.allowValidation = false;
19821
- this.filterTypes = FilterType;
19822
- this.submitted = false;
19823
- this.hooked = false;
20157
+ this.entityFacadeRegistryService = entityFacadeRegistryService;
20158
+ this.filterList = [];
20159
+ this.viewFilterText = "";
20160
+ this.canEditViewFilter = false;
20161
+ this.editingViewFilter = false;
20162
+ this.filterType = FilterType;
20163
+ this.Fields = [];
20164
+ this.DisplayFields = this.Fields;
20165
+ this.currentCriteria = null;
20166
+ this.hiddenFilters = [];
19824
20167
  this.destroy$ = new Subject();
19825
- this.cascadeTrigger$ = new Subject();
19826
- this.facadeReadOnly = false;
19827
- this.entityReadonly = false;
19828
- this.icon = "circle-info-solid";
19829
- this.isLoadingExtended = false;
19830
- this.iconUrl = "pencil.svg";
19831
- }
19832
- get Fields() {
19833
- return (this.ViewModel?.FieldSections ?? []).map((s) => s.FieldViewModels).reduce((a, b) => [...a, ...b], []);
19834
20168
  }
19835
20169
  ngOnInit() {
19836
20170
  this.facade = this.entityFacadeRegistryService.getFacade(this.entityKey, this.guid);
19837
- const isStandalone = this.facade.editor.isStandaloneMode$.value;
19838
- const id = this.route.snapshot.params["id"] ?? null;
19839
- this.facade.config.viewModel$.pipe(takeUntil$1(this.destroy$)).subscribe((cfgVm) => {
19840
- this.ViewModel = cfgVm;
19841
- this.facade.sections.setViewModel(cfgVm);
19842
- this.initForm(true);
19843
- });
19844
- this.viewModel$ = this.vmLoader.getViewModel$(this.entityKey, this.guid, isStandalone, id);
19845
- this.viewModel$.pipe(takeUntil$1(this.destroy$)).subscribe((vm) => {
19846
- this.onSelectionChanged(vm);
19847
- });
19848
- this.facade.sections.extendedProperties$.pipe(takeUntil$1(this.destroy$)).subscribe((res) => this.applyExtendedProperties(res));
19849
- this.facade.editor.readOnlyMode$.pipe(takeUntil$1(this.destroy$)).subscribe((v) => {
19850
- this.facadeReadOnly = v;
19851
- this.updateReadonlyState();
19852
- });
19853
- this.facade.selection.cleared$.pipe(takeUntil$1(this.destroy$)).subscribe(() => this.onSelectionCleared());
19854
- this.cascadeTrigger$.pipe(takeUntil$1(this.destroy$)).subscribe((fields) => {
19855
- fields.forEach((f) => {
19856
- this.alterFieldCascadeData(f);
19857
- this.handleCascadeField(f);
19858
- });
19859
- });
19860
- this.facade.change.getStyleUpdated$().pipe(takeUntil$1(this.destroy$)).subscribe((entities) => {
19861
- const entity = entities?.[0];
19862
- if (!entity) return;
19863
- if (!this.facade.editor.isStandaloneMode$.value && !this.facade.selection.getSelected().some((s) => s.Id === entity.Id)) return;
19864
- this.patchFormFromEntity(entity);
20171
+ this.wrapper = new FormWrapper(this.validationService, this.bus, false);
20172
+ combineLatest([
20173
+ this.facade.criteria$,
20174
+ this.facade.config.filterDescriptors$,
20175
+ this.facade.layout.hiddenSummaryFilters$,
20176
+ this.facade.filter.viewFilter$,
20177
+ this.facade.filter.viewFilterEditing$,
20178
+ this.facade.config.columns$.pipe(startWith([]))
20179
+ ]).pipe(takeUntil(this.destroy$)).subscribe(([vm, fields, hidden, viewFilter, editing, columns]) => {
20180
+ this.canEditViewFilter = !!viewFilter?.canEdit;
20181
+ this.editingViewFilter = editing;
20182
+ const filters = fields.filter((x) => x.PropertyName != "Keywords" && x.PropertyName != "Id");
20183
+ this.Fields = filters;
20184
+ this.hiddenFilters = editing ? hidden : [...hidden, ...Object.keys(viewFilter?.filter.Panel ?? {})];
20185
+ this.viewFilterText = viewFilter ? this.describeViewFilter(viewFilter.label, viewFilter.filter, fields, columns ?? []) : "";
20186
+ this.currentCriteria = vm;
20187
+ this.wrapper.initialize(filters);
20188
+ this.wrapper.hookFormEntity(vm);
20189
+ this.updateSummary();
19865
20190
  });
19866
20191
  }
19867
- set dynamicTabsTemplates(content) {
19868
- if (content) this.facade.sections.registerDynamicContainers(content.toArray());
20192
+ ngOnDestroy() {
20193
+ this.destroy$.next();
20194
+ this.destroy$.complete();
19869
20195
  }
19870
- set tabs(content) {
19871
- if (content) this.facade.sections.registerTabset(content);
20196
+ updateSummary() {
20197
+ const descriptors = this.Fields;
20198
+ if (descriptors) {
20199
+ this.filterList.splice(0, this.filterList.length);
20200
+ descriptors.filter((field) => !this.hiddenFilters.includes(field.PropertyName)).forEach((field) => {
20201
+ this.updateFieldSummary(field);
20202
+ });
20203
+ }
19872
20204
  }
19873
- initForm(withValidators = false) {
19874
- if (!this.propertyForm) this.propertyForm = new FormGroup({});
19875
- this.Fields.forEach((field) => {
19876
- const validators = withValidators ? this.validationService.getFieldValidators(field) : [];
19877
- if (!this.propertyForm.contains(field.PropertyName)) {
19878
- const ctrl = new FormControl(null, validators);
19879
- this.propertyForm.addControl(field.PropertyName, ctrl);
19880
- } else {
19881
- const ctrl = this.propertyForm.get(field.PropertyName);
19882
- ctrl.setValidators(validators);
19883
- ctrl.updateValueAndValidity({ emitEvent: false });
19884
- }
20205
+ updateFieldSummary(field) {
20206
+ const displayedVal = this.getFilterSummaryValue(field);
20207
+ if (displayedVal !== null && displayedVal !== void 0) this.filterList.push({
20208
+ DisplayedValue: displayedVal,
20209
+ Field: field
19885
20210
  });
19886
- this.facade.change.setForm(this.propertyForm);
19887
20211
  }
19888
- patchFormFromEntity(entity) {
19889
- if (!this.propertyForm) return;
19890
- this.propertyForm.patchValue(entity, { emitEvent: false });
20212
+ getFilterSummaryValue(field) {
20213
+ if (!field) return null;
20214
+ const fv = this.currentCriteria != null ? this.currentCriteria[field.PropertyName] : this.wrapper.form.get(field.PropertyName)?.value;
20215
+ if (Utilities.IsNil(fv) || fv === -1 || parseInt(fv) === -1 || Utilities.isEmptyString(fv)) return;
20216
+ let displayedValue = null;
20217
+ if (field.FilterType === FilterType.ComboBox) {
20218
+ if (!Utilities.IsUndefined(field.OriginalData)) {
20219
+ const fld = field.OriginalData.find((p) => p[field.ValueMember] == fv);
20220
+ if (fld && fld.hasOwnProperty(field.DisplayMember)) displayedValue = `${fld[field.DisplayMember]}`;
20221
+ }
20222
+ } else if (field.FilterType === FilterType.String || field.FilterType === FilterType.Integer) displayedValue = `${fv}`;
20223
+ else return null;
20224
+ return displayedValue;
19891
20225
  }
19892
- onSelectionCleared() {
19893
- if (this.propertyForm) {
20226
+ resetFormControl(field) {
20227
+ if (!field.IsClearable || field.PropertyName in this.facade.filter.lockedPanel) return;
20228
+ const currentValue = this.currentCriteria != null ? this.currentCriteria[field.PropertyName] : this.wrapper.form.get(field.PropertyName)?.value;
20229
+ if (Utilities.IsNil(currentValue) || currentValue === -1 || Utilities.isEmptyString(currentValue)) return;
20230
+ let newValue;
20231
+ if (field.FilterType === FilterType.ComboBox) newValue = field.Data?.find((d) => d[field.ValueMember] === -1) ? -1 : null;
20232
+ else if (field.FilterType === FilterType.String) newValue = "";
20233
+ else return;
20234
+ this.wrapper.form.get(field.PropertyName).reset(newValue, { emitEvent: false });
20235
+ this.facade.filter.setFilter(this.wrapper.form.getRawValue());
20236
+ }
20237
+ describeViewFilter(label, filter, fields, columns) {
20238
+ const parts = [...this.describePanel(filter.Panel ?? {}, fields), ...this.describeGroup({
20239
+ ...filter,
20240
+ Panel: void 0
20241
+ }, columns, true) ? [this.describeGroup({
20242
+ ...filter,
20243
+ Panel: void 0
20244
+ }, columns, true)] : []];
20245
+ return parts.length ? `Vue « ${label} » : ${parts.join(" ; ")}` : "";
20246
+ }
20247
+ describePanel(panel, fields) {
20248
+ return Object.entries(panel).map(([property, value]) => {
20249
+ const field = fields.find((f) => f.PropertyName === property);
20250
+ const data = Array.isArray(field?.OriginalData) ? field.OriginalData : Array.isArray(field?.Data) ? field.Data : [];
20251
+ const item = field?.ValueMember ? data.find((d) => d?.[field.ValueMember] == value) : void 0;
20252
+ const shown = item && field?.DisplayMember ? item[field.DisplayMember] : value === true ? "Oui" : value === false ? "Non" : value === null || value === -1 ? "Tous" : value;
20253
+ return `${field?.Label || property} : ${shown}`;
20254
+ });
20255
+ }
20256
+ describeGroup(group, columns, root = false) {
20257
+ const label = (property) => columns.find((c) => c.Property === property)?.Name || property;
20258
+ const operator = (op) => Object.values(FilterOperators).flat().find((o) => o.value === op)?.label ?? (op === "like" ? "ressemble à" : op);
20259
+ const conditions = (group.Conditions ?? []).map((c) => {
20260
+ const values = c.Operator === "in" || c.Operator === "notin" ? (c.Values ?? []).join(", ") : c.Operator === "between" || c.Operator === "datebetween" ? `${c.Value ?? "…"} et ${c.Value2 ?? "…"}` : c.Value ?? "";
20261
+ return `${label(c.Property)} ${operator(c.Operator)}${values ? " " + values : ""}`.trim();
20262
+ });
20263
+ const groups = (group.Groups ?? []).map((g) => this.describeGroup(g, columns)).filter((t) => !!t);
20264
+ const all = [...conditions, ...groups];
20265
+ if (!all.length) return "";
20266
+ const text = all.join(group.Logic === "or" ? " ou " : " et ");
20267
+ return root || all.length === 1 ? text : `(${text})`;
20268
+ }
20269
+ resetFieldData(field) {
20270
+ const fv = this.wrapper.form.get(field.PropertyName).value;
20271
+ field.Data = field.OriginalData.filter((p) => p[field.ValueMember] === -1 || Object.keys(field.CurrentFilters).every((name) => {
20272
+ return p[name] === fv;
20273
+ }));
20274
+ }
20275
+ static {
20276
+ this.ɵfac = i0.ɵɵngDeclareFactory({
20277
+ minVersion: "12.0.0",
20278
+ version: "22.2.1",
20279
+ ngImport: i0,
20280
+ type: FilterSummaryPanelComponent,
20281
+ deps: [
20282
+ { token: ValidationService },
20283
+ { token: PropertyChangeBus },
20284
+ { token: EntityFacadeRegistryService }
20285
+ ],
20286
+ target: i0.ɵɵFactoryTarget.Component
20287
+ });
20288
+ }
20289
+ static {
20290
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
20291
+ minVersion: "14.0.0",
20292
+ version: "22.2.1",
20293
+ type: FilterSummaryPanelComponent,
20294
+ isStandalone: false,
20295
+ selector: "alg-filter-summary-panel",
20296
+ inputs: {
20297
+ entityKey: "entityKey",
20298
+ guid: "guid"
20299
+ },
20300
+ ngImport: i0,
20301
+ template: "<!-- <div class=\"clearfix\" *ngIf=\"!isEmptyFilter()\">\r\n <div class=\"filter-summary\">\r\n <div class=\"filter\" [formGroup]=\"wrapper.form\">\r\n <ng-container *ngIf=\"filterList\">\r\n \r\n <div *ngFor=\"let field of Fields\" class=\"option\">\r\n <div [ngSwitch]=\"field.FilterType\">\r\n <div *ngSwitchCase=\"filterType.String\" class=\"form-group\">\r\n <input\r\n class=\"form-control\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n [readonly]=\"field.ReadOnly\"\r\n [type]=\"field.InputType\"\r\n />\r\n </div>\r\n <div\r\n *ngSwitchCase=\"filterType.Integer\"\r\n class=\"form-group\"\r\n >\r\n <div>\r\n <input\r\n class=\"form-control\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n type=\"number\"\r\n focusable\r\n ng-pattern=\"/^[0-9]+$/\"\r\n tabindex=\"0\"\r\n />\r\n </div>\r\n <div *ngSwitchCase=\"filterType.ComboBox\" class=\"form-group\">\r\n <div>\r\n <ng-select\r\n appendTo=\"body\"\r\n placeholder=\"Sélectionner {{\r\n field.Label\r\n }}\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n [items]=\"field.Data\"\r\n [multiple]=\"field.MultipleSelection\"\r\n [hideSelected]=\"field.HideSelected\"\r\n bindValue=\"{{ field.ValueMember }}\"\r\n [clearable]=\"field.IsClearable\"\r\n bindLabel=\"{{ field.DisplayMember }}\"\r\n [selectOnTab]=\"true\"\r\n [readonly]=\"field.ReadOnly\"\r\n >\r\n </ng-select>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</div> -->\r\n\r\n\r\n<div class=\"clearfix\" *ngIf=\"!isEmptyFilter()\">\r\n <!-- Filtre fixé par la vue affichée : non modifiable à l'écran. -->\r\n <div class=\"view-filter\" *ngIf=\"viewFilterText\" [class.editing]=\"editingViewFilter\">\r\n <i class=\"fa\" [class.fa-lock]=\"!editingViewFilter\" [class.fa-unlock]=\"editingViewFilter\"></i>\r\n <span class=\"view-filter-text\" [title]=\"viewFilterText\">{{ viewFilterText }}</span>\r\n <!-- Mode édition (propriétaire de la vue ou administrateur) : filtres débloqués, puis réenregistrés. -->\r\n <ng-container *ngIf=\"canEditViewFilter\">\r\n <button *ngIf=\"!editingViewFilter\" type=\"button\" class=\"view-filter-btn\" title=\"Débloquer les filtres de la vue pour les modifier\"\r\n (click)=\"editViewFilter()\"><i class=\"fa fa-pencil\"></i> Modifier</button>\r\n <ng-container *ngIf=\"editingViewFilter\">\r\n <button type=\"button\" class=\"view-filter-btn primary\" title=\"Enregistrer dans la vue le tri et les filtres de l'écran\"\r\n (click)=\"saveViewFilter()\"><i class=\"fa fa-check\"></i> Enregistrer</button>\r\n <button type=\"button\" class=\"view-filter-btn\" title=\"Revenir aux filtres de la vue\" (click)=\"cancelViewFilter()\">Annuler</button>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n <div class=\"filter-summary\" *ngIf=\"filterList.length\">\r\n <div class=\"filter\">\r\n <ng-container *ngIf=\"filterList\">\r\n <div *ngFor=\"let field of filterList\">\r\n <div class=\"option\" [class.clearable]=\"field.Field.IsClearable\" (click)=\"resetFormControl(field.Field)\">\r\n {{field.Field.Label + ' : '+ field.DisplayedValue}}\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</div>\r\n",
20302
+ styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}.view-filter{display:flex;align-items:center;gap:6px;padding:4px 8px;border-bottom:solid 1px var(--main-bg-color);color:#4465c9;font-size:12px}.view-filter>.fa{opacity:.7}.view-filter.editing{background:#ebf0ff}.view-filter-text{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.view-filter-btn{flex:none;padding:1px 8px;border:1px solid #DBE4FF;border-radius:4px;background:#fff;color:#4465c9;font-size:12px;cursor:pointer}.view-filter-btn.primary{background:#4465c9;border-color:#4465c9;color:#fff}.filter-summary{padding:2px 3px;border-bottom:solid 1px var(--main-bg-color);display:flex}.option alg-field .form-group{display:flex!important}.filter-summary .filter{display:flex;flex-direction:row;padding:2px}.filter-summary .filter .option{display:block;float:left;position:relative;background-color:#dbe4ff;text-transform:capitalize;color:#4465c9;padding:5px 10px;margin:0 5px 0 0;border-radius:20px;cursor:pointer;-webkit-transition:background-color .1s ease-out;-moz-transition:background-color .1s ease-out;-o-transition:background-color .1s ease-out;transition:background-color .1s ease-out;border-left:1px solid #ffffff}.filter-summary .filter .option.clearable:hover{text-decoration:line-through}.filter-summary .filter:before,.filter-summary .filter:after{content:\"\";display:table}.filter-summary:before,.filter-summary:after{content:\"\";display:table}\n"],
20303
+ dependencies: [{
20304
+ kind: "directive",
20305
+ type: i1$5.NgForOf,
20306
+ selector: "[ngFor][ngForOf]",
20307
+ inputs: [
20308
+ "ngForOf",
20309
+ "ngForTrackBy",
20310
+ "ngForTemplate"
20311
+ ]
20312
+ }, {
20313
+ kind: "directive",
20314
+ type: i1$5.NgIf,
20315
+ selector: "[ngIf]",
20316
+ inputs: [
20317
+ "ngIf",
20318
+ "ngIfThen",
20319
+ "ngIfElse"
20320
+ ]
20321
+ }],
20322
+ changeDetection: i0.ChangeDetectionStrategy.Eager
20323
+ });
20324
+ }
20325
+ };
20326
+ i0.ɵɵngDeclareClassMetadata({
20327
+ minVersion: "12.0.0",
20328
+ version: "22.2.1",
20329
+ ngImport: i0,
20330
+ type: FilterSummaryPanelComponent,
20331
+ decorators: [{
20332
+ type: Component,
20333
+ args: [{
20334
+ selector: "alg-filter-summary-panel",
20335
+ changeDetection: ChangeDetectionStrategy.Eager,
20336
+ standalone: false,
20337
+ template: "<!-- <div class=\"clearfix\" *ngIf=\"!isEmptyFilter()\">\r\n <div class=\"filter-summary\">\r\n <div class=\"filter\" [formGroup]=\"wrapper.form\">\r\n <ng-container *ngIf=\"filterList\">\r\n \r\n <div *ngFor=\"let field of Fields\" class=\"option\">\r\n <div [ngSwitch]=\"field.FilterType\">\r\n <div *ngSwitchCase=\"filterType.String\" class=\"form-group\">\r\n <input\r\n class=\"form-control\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n [readonly]=\"field.ReadOnly\"\r\n [type]=\"field.InputType\"\r\n />\r\n </div>\r\n <div\r\n *ngSwitchCase=\"filterType.Integer\"\r\n class=\"form-group\"\r\n >\r\n <div>\r\n <input\r\n class=\"form-control\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n type=\"number\"\r\n focusable\r\n ng-pattern=\"/^[0-9]+$/\"\r\n tabindex=\"0\"\r\n />\r\n </div>\r\n <div *ngSwitchCase=\"filterType.ComboBox\" class=\"form-group\">\r\n <div>\r\n <ng-select\r\n appendTo=\"body\"\r\n placeholder=\"Sélectionner {{\r\n field.Label\r\n }}\"\r\n [formControlName]=\"field.PropertyName\"\r\n [id]=\"field.PropertyName\"\r\n [items]=\"field.Data\"\r\n [multiple]=\"field.MultipleSelection\"\r\n [hideSelected]=\"field.HideSelected\"\r\n bindValue=\"{{ field.ValueMember }}\"\r\n [clearable]=\"field.IsClearable\"\r\n bindLabel=\"{{ field.DisplayMember }}\"\r\n [selectOnTab]=\"true\"\r\n [readonly]=\"field.ReadOnly\"\r\n >\r\n </ng-select>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</div> -->\r\n\r\n\r\n<div class=\"clearfix\" *ngIf=\"!isEmptyFilter()\">\r\n <!-- Filtre fixé par la vue affichée : non modifiable à l'écran. -->\r\n <div class=\"view-filter\" *ngIf=\"viewFilterText\" [class.editing]=\"editingViewFilter\">\r\n <i class=\"fa\" [class.fa-lock]=\"!editingViewFilter\" [class.fa-unlock]=\"editingViewFilter\"></i>\r\n <span class=\"view-filter-text\" [title]=\"viewFilterText\">{{ viewFilterText }}</span>\r\n <!-- Mode édition (propriétaire de la vue ou administrateur) : filtres débloqués, puis réenregistrés. -->\r\n <ng-container *ngIf=\"canEditViewFilter\">\r\n <button *ngIf=\"!editingViewFilter\" type=\"button\" class=\"view-filter-btn\" title=\"Débloquer les filtres de la vue pour les modifier\"\r\n (click)=\"editViewFilter()\"><i class=\"fa fa-pencil\"></i> Modifier</button>\r\n <ng-container *ngIf=\"editingViewFilter\">\r\n <button type=\"button\" class=\"view-filter-btn primary\" title=\"Enregistrer dans la vue le tri et les filtres de l'écran\"\r\n (click)=\"saveViewFilter()\"><i class=\"fa fa-check\"></i> Enregistrer</button>\r\n <button type=\"button\" class=\"view-filter-btn\" title=\"Revenir aux filtres de la vue\" (click)=\"cancelViewFilter()\">Annuler</button>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n <div class=\"filter-summary\" *ngIf=\"filterList.length\">\r\n <div class=\"filter\">\r\n <ng-container *ngIf=\"filterList\">\r\n <div *ngFor=\"let field of filterList\">\r\n <div class=\"option\" [class.clearable]=\"field.Field.IsClearable\" (click)=\"resetFormControl(field.Field)\">\r\n {{field.Field.Label + ' : '+ field.DisplayedValue}}\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</div>\r\n",
20338
+ styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}.view-filter{display:flex;align-items:center;gap:6px;padding:4px 8px;border-bottom:solid 1px var(--main-bg-color);color:#4465c9;font-size:12px}.view-filter>.fa{opacity:.7}.view-filter.editing{background:#ebf0ff}.view-filter-text{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.view-filter-btn{flex:none;padding:1px 8px;border:1px solid #DBE4FF;border-radius:4px;background:#fff;color:#4465c9;font-size:12px;cursor:pointer}.view-filter-btn.primary{background:#4465c9;border-color:#4465c9;color:#fff}.filter-summary{padding:2px 3px;border-bottom:solid 1px var(--main-bg-color);display:flex}.option alg-field .form-group{display:flex!important}.filter-summary .filter{display:flex;flex-direction:row;padding:2px}.filter-summary .filter .option{display:block;float:left;position:relative;background-color:#dbe4ff;text-transform:capitalize;color:#4465c9;padding:5px 10px;margin:0 5px 0 0;border-radius:20px;cursor:pointer;-webkit-transition:background-color .1s ease-out;-moz-transition:background-color .1s ease-out;-o-transition:background-color .1s ease-out;transition:background-color .1s ease-out;border-left:1px solid #ffffff}.filter-summary .filter .option.clearable:hover{text-decoration:line-through}.filter-summary .filter:before,.filter-summary .filter:after{content:\"\";display:table}.filter-summary:before,.filter-summary:after{content:\"\";display:table}\n"]
20339
+ }]
20340
+ }],
20341
+ ctorParameters: () => [
20342
+ { type: ValidationService },
20343
+ { type: PropertyChangeBus },
20344
+ { type: EntityFacadeRegistryService }
20345
+ ],
20346
+ propDecorators: {
20347
+ entityKey: [{ type: Input }],
20348
+ guid: [{ type: Input }]
20349
+ }
20350
+ });
20351
+ var ViewModelLoaderService = class ViewModelLoaderService {
20352
+ constructor(facades) {
20353
+ this.facades = facades;
20354
+ }
20355
+ getViewModel$(entityKey, guid, isStandalone = true, id) {
20356
+ const facade = this.facades.getFacade(entityKey, guid);
20357
+ const config$ = facade.config.viewModel$.pipe(filter((vm) => !!vm?.FieldSections?.length), shareReplay({
20358
+ bufferSize: 1,
20359
+ refCount: true
20360
+ }));
20361
+ if (isStandalone && (!id || id === "0")) return config$.pipe(map((cfg) => this.createNew(cfg)), shareReplay({
20362
+ bufferSize: 1,
20363
+ refCount: true
20364
+ }));
20365
+ if (isStandalone && id && id !== "0") {
20366
+ facade.selection.getSelectedById$(id);
20367
+ return facade.config.getApiViewModel(id);
20368
+ }
20369
+ return combineLatest([config$, facade.selection.selected$]).pipe(map(([cfg, selection]) => {
20370
+ const adapted = facade.selection.adaptSelectionToViewModel(selection, cfg.FieldSections);
20371
+ return {
20372
+ ...cfg,
20373
+ ViewModel: adapted ?? cfg.ViewModel,
20374
+ FieldSections: cfg.FieldSections
20375
+ };
20376
+ }), shareReplay({
20377
+ bufferSize: 1,
20378
+ refCount: true
20379
+ }));
20380
+ }
20381
+ createNew(baseVm) {
20382
+ const clone = {
20383
+ ...baseVm.ViewModel,
20384
+ Id: null
20385
+ };
20386
+ return {
20387
+ ...baseVm,
20388
+ ViewModel: clone,
20389
+ FieldSections: baseVm.FieldSections
20390
+ };
20391
+ }
20392
+ static {
20393
+ this.ɵfac = i0.ɵɵngDeclareFactory({
20394
+ minVersion: "12.0.0",
20395
+ version: "22.2.1",
20396
+ ngImport: i0,
20397
+ type: ViewModelLoaderService,
20398
+ deps: [{ token: EntityFacadeRegistryService }],
20399
+ target: i0.ɵɵFactoryTarget.Injectable
20400
+ });
20401
+ }
20402
+ static {
20403
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
20404
+ minVersion: "12.0.0",
20405
+ version: "22.2.1",
20406
+ ngImport: i0,
20407
+ type: ViewModelLoaderService
20408
+ });
20409
+ }
20410
+ };
20411
+ i0.ɵɵngDeclareClassMetadata({
20412
+ minVersion: "12.0.0",
20413
+ version: "22.2.1",
20414
+ ngImport: i0,
20415
+ type: ViewModelLoaderService,
20416
+ decorators: [{ type: Injectable }],
20417
+ ctorParameters: () => [{ type: EntityFacadeRegistryService }]
20418
+ });
20419
+ var PropertyEditorComponent = class PropertyEditorComponent {
20420
+ handleKeywordsChanged(event) {}
20421
+ isFieldHidden(field) {
20422
+ return !(Utilities.IsNil(this.keyword) || field.Label.toUpperCase().includes(this.keyword.toUpperCase()));
20423
+ }
20424
+ isSectionHidden(section) {
20425
+ return !(Utilities.IsNil(this.keyword) || section.Name.toUpperCase().includes(this.keyword.toUpperCase()) || section.FieldViewModels.some((f) => !this.isFieldHidden(f)));
20426
+ }
20427
+ constructor(elementRef, toastrService, validationService, vmLoader, entityFacadeRegistryService, route, bus) {
20428
+ this.elementRef = elementRef;
20429
+ this.toastrService = toastrService;
20430
+ this.validationService = validationService;
20431
+ this.vmLoader = vmLoader;
20432
+ this.entityFacadeRegistryService = entityFacadeRegistryService;
20433
+ this.route = route;
20434
+ this.bus = bus;
20435
+ this.validateAfterEachChange = true;
20436
+ this.searchText = "Rechercher une propriété...";
20437
+ this.readonly = false;
20438
+ this.fileChange$ = new Subject();
20439
+ this.selectorChange$ = new Subject();
20440
+ this.buttonClick$ = new Subject();
20441
+ this.allowValidation = false;
20442
+ this.filterTypes = FilterType;
20443
+ this.submitted = false;
20444
+ this.hooked = false;
20445
+ this.destroy$ = new Subject();
20446
+ this.cascadeTrigger$ = new Subject();
20447
+ this.facadeReadOnly = false;
20448
+ this.entityReadonly = false;
20449
+ this.icon = "circle-info-solid";
20450
+ this.isLoadingExtended = false;
20451
+ this.iconUrl = "pencil.svg";
20452
+ }
20453
+ get Fields() {
20454
+ return (this.ViewModel?.FieldSections ?? []).map((s) => s.FieldViewModels).reduce((a, b) => [...a, ...b], []);
20455
+ }
20456
+ ngOnInit() {
20457
+ this.facade = this.entityFacadeRegistryService.getFacade(this.entityKey, this.guid);
20458
+ const isStandalone = this.facade.editor.isStandaloneMode$.value;
20459
+ const id = this.route.snapshot.params["id"] ?? null;
20460
+ this.facade.config.viewModel$.pipe(takeUntil$1(this.destroy$)).subscribe((cfgVm) => {
20461
+ this.ViewModel = cfgVm;
20462
+ this.facade.sections.setViewModel(cfgVm);
20463
+ this.initForm(true);
20464
+ });
20465
+ this.viewModel$ = this.vmLoader.getViewModel$(this.entityKey, this.guid, isStandalone, id);
20466
+ this.viewModel$.pipe(takeUntil$1(this.destroy$)).subscribe((vm) => {
20467
+ this.onSelectionChanged(vm);
20468
+ });
20469
+ this.facade.sections.extendedProperties$.pipe(takeUntil$1(this.destroy$)).subscribe((res) => this.applyExtendedProperties(res));
20470
+ this.facade.editor.readOnlyMode$.pipe(takeUntil$1(this.destroy$)).subscribe((v) => {
20471
+ this.facadeReadOnly = v;
20472
+ this.updateReadonlyState();
20473
+ });
20474
+ this.facade.selection.cleared$.pipe(takeUntil$1(this.destroy$)).subscribe(() => this.onSelectionCleared());
20475
+ this.cascadeTrigger$.pipe(takeUntil$1(this.destroy$)).subscribe((fields) => {
20476
+ fields.forEach((f) => {
20477
+ this.alterFieldCascadeData(f);
20478
+ this.handleCascadeField(f);
20479
+ });
20480
+ });
20481
+ this.facade.change.getStyleUpdated$().pipe(takeUntil$1(this.destroy$)).subscribe((entities) => {
20482
+ const entity = entities?.[0];
20483
+ if (!entity) return;
20484
+ if (!this.facade.editor.isStandaloneMode$.value && !this.facade.selection.getSelected().some((s) => s.Id === entity.Id)) return;
20485
+ this.patchFormFromEntity(entity);
20486
+ });
20487
+ }
20488
+ set dynamicTabsTemplates(content) {
20489
+ if (content) this.facade.sections.registerDynamicContainers(content.toArray());
20490
+ }
20491
+ set tabs(content) {
20492
+ if (content) this.facade.sections.registerTabset(content);
20493
+ }
20494
+ initForm(withValidators = false) {
20495
+ if (!this.propertyForm) this.propertyForm = new FormGroup({});
20496
+ this.Fields.forEach((field) => {
20497
+ const validators = withValidators ? this.validationService.getFieldValidators(field) : [];
20498
+ if (!this.propertyForm.contains(field.PropertyName)) {
20499
+ const ctrl = new FormControl(null, validators);
20500
+ this.propertyForm.addControl(field.PropertyName, ctrl);
20501
+ } else {
20502
+ const ctrl = this.propertyForm.get(field.PropertyName);
20503
+ ctrl.setValidators(validators);
20504
+ ctrl.updateValueAndValidity({ emitEvent: false });
20505
+ }
20506
+ });
20507
+ this.facade.change.setForm(this.propertyForm);
20508
+ }
20509
+ patchFormFromEntity(entity) {
20510
+ if (!this.propertyForm) return;
20511
+ this.propertyForm.patchValue(entity, { emitEvent: false });
20512
+ }
20513
+ onSelectionCleared() {
20514
+ if (this.propertyForm) {
19894
20515
  this.propertyForm.reset();
19895
20516
  this.propertyForm.clearValidators();
19896
20517
  }
@@ -20905,7 +21526,7 @@ var CardListComponent = class CardListComponent {
20905
21526
  },
20906
21527
  ngImport: i0,
20907
21528
  template: "<div class=\"card-list\" (scroll)=\"onScroll($event)\" role=\"list\">\r\n <div *ngIf=\"card === null\" class=\"card-info\">Pas de vue Carte pour cette table.</div>\r\n\r\n <ng-container *ngIf=\"card\">\r\n <button type=\"button\" *ngFor=\"let row of items; trackBy: trackById\" class=\"card-item\" role=\"listitem\"\r\n [class.selected]=\"isSelected(row)\" [class.no-image]=\"!hasImage()\" (click)=\"open(row)\">\r\n <span class=\"card-image\" *ngIf=\"hasImage()\">\r\n <img *ngIf=\"imageUrl(row) as url; else initialsTpl\" [src]=\"url\" alt=\"\" />\r\n <ng-template #initialsTpl><span class=\"card-initials\">{{ initials(row) }}</span></ng-template>\r\n </span>\r\n <span class=\"card-main\">\r\n <span class=\"card-title\" *ngIf=\"card.title\">{{ valueOf(row, card.title) }}</span>\r\n <span class=\"card-text\" *ngIf=\"card.text\">{{ valueOf(row, card.text) }}</span>\r\n <span class=\"card-detail\" *ngIf=\"card.detail\">{{ valueOf(row, card.detail) }}</span>\r\n </span>\r\n <span class=\"card-badge\" *ngIf=\"card.badge && valueOf(row, card.badge) as badge\">{{ badge }}</span>\r\n </button>\r\n\r\n <div class=\"card-info\" *ngIf=\"!items.length && !loading\">Aucun élément</div>\r\n <button type=\"button\" class=\"card-more\" *ngIf=\"hasMore\" [disabled]=\"loading\" (click)=\"loadMore()\">\r\n {{ loading ? 'Chargement…' : 'Afficher les suivants' }}\r\n </button>\r\n </ng-container>\r\n</div>\r\n",
20908
- styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;height:100%}.card-list{flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:8px;padding:10px;background:#f4f6fb}.card-item{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:12px;align-items:center;width:100%;padding:10px 12px;border:1px solid #DBE4FF;border-radius:4px 0 0 4px;background:#fff;color:#4d5a81;font:inherit;text-align:left;cursor:pointer}.card-item.no-image{grid-template-columns:minmax(0,1fr) auto}.card-item:hover{border-color:#5272d3}.card-item:focus-visible{outline:2px solid #5272D3;outline-offset:1px}.card-item.selected{border-color:#5272d3;box-shadow:inset 3px 0 #5272d3}.card-image{width:44px;height:44px;border-radius:4px;background:#ebf0ff;display:grid;place-items:center;overflow:hidden}.card-image img{max-width:100%;max-height:100%;object-fit:contain}.card-initials{color:#4465c9;font-weight:700;font-size:13px}.card-main{min-width:0;display:flex;flex-direction:column}.card-title,.card-text,.card-detail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-title{font-size:12px;color:#4465c9}.card-text{font-size:14px;font-weight:700}.card-detail{font-size:12px;color:#9fa9c7}.card-badge{padding:1px 8px;border-radius:999px;background:#ebf0ff;color:#4465c9;font-size:11px;font-weight:700;white-space:nowrap}.card-info{padding:16px;text-align:center;color:#9fa9c7}.card-more{align-self:center;margin:4px 0 8px;padding:6px 14px;border:1px solid #C1D0FD;border-radius:999px;background:#fff;color:#4465c9;font:inherit;cursor:pointer}.card-more:disabled{cursor:progress;opacity:.7}\n"],
21529
+ styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;height:100%}.card-list{flex:1 1 auto;min-height:0;overflow-y:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(300px,100%),1fr));align-content:start;gap:8px;padding:10px;background:#f4f6fb}.card-list>.card-info,.card-list>.card-more{grid-column:1/-1}.card-item{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:12px;align-items:center;width:100%;padding:10px 12px;border:1px solid #DBE4FF;border-radius:0 4px 4px 0;background:#fff;color:#4d5a81;font:inherit;text-align:left;cursor:pointer}.card-item.no-image{grid-template-columns:minmax(0,1fr) auto}.card-item:hover{border-color:#5272d3}.card-item:focus-visible{outline:2px solid #5272D3;outline-offset:1px}.card-item.selected{border-color:#5272d3;box-shadow:inset 3px 0 #5272d3}.card-image{width:44px;height:44px;border-radius:4px;background:#ebf0ff;display:grid;place-items:center;overflow:hidden}.card-image img{max-width:100%;max-height:100%;object-fit:contain}.card-initials{color:#4465c9;font-weight:700;font-size:13px}.card-main{min-width:0;display:flex;flex-direction:column}.card-title,.card-text,.card-detail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-title{font-size:12px;color:#4465c9}.card-text{font-size:14px;font-weight:700}.card-detail{font-size:12px;color:#9fa9c7}.card-badge{padding:1px 8px;border-radius:999px;background:#ebf0ff;color:#4465c9;font-size:11px;font-weight:700;white-space:nowrap}.card-info{padding:16px;text-align:center;color:#9fa9c7}.card-more{justify-self:center;margin:4px 0 8px;padding:6px 14px;border:1px solid #C1D0FD;border-radius:999px;background:#fff;color:#4465c9;font:inherit;cursor:pointer}.card-more:disabled{cursor:progress;opacity:.7}\n"],
20909
21530
  dependencies: [{
20910
21531
  kind: "directive",
20911
21532
  type: i1$5.NgForOf,
@@ -20941,7 +21562,7 @@ i0.ɵɵngDeclareClassMetadata({
20941
21562
  changeDetection: ChangeDetectionStrategy.Eager,
20942
21563
  standalone: false,
20943
21564
  template: "<div class=\"card-list\" (scroll)=\"onScroll($event)\" role=\"list\">\r\n <div *ngIf=\"card === null\" class=\"card-info\">Pas de vue Carte pour cette table.</div>\r\n\r\n <ng-container *ngIf=\"card\">\r\n <button type=\"button\" *ngFor=\"let row of items; trackBy: trackById\" class=\"card-item\" role=\"listitem\"\r\n [class.selected]=\"isSelected(row)\" [class.no-image]=\"!hasImage()\" (click)=\"open(row)\">\r\n <span class=\"card-image\" *ngIf=\"hasImage()\">\r\n <img *ngIf=\"imageUrl(row) as url; else initialsTpl\" [src]=\"url\" alt=\"\" />\r\n <ng-template #initialsTpl><span class=\"card-initials\">{{ initials(row) }}</span></ng-template>\r\n </span>\r\n <span class=\"card-main\">\r\n <span class=\"card-title\" *ngIf=\"card.title\">{{ valueOf(row, card.title) }}</span>\r\n <span class=\"card-text\" *ngIf=\"card.text\">{{ valueOf(row, card.text) }}</span>\r\n <span class=\"card-detail\" *ngIf=\"card.detail\">{{ valueOf(row, card.detail) }}</span>\r\n </span>\r\n <span class=\"card-badge\" *ngIf=\"card.badge && valueOf(row, card.badge) as badge\">{{ badge }}</span>\r\n </button>\r\n\r\n <div class=\"card-info\" *ngIf=\"!items.length && !loading\">Aucun élément</div>\r\n <button type=\"button\" class=\"card-more\" *ngIf=\"hasMore\" [disabled]=\"loading\" (click)=\"loadMore()\">\r\n {{ loading ? 'Chargement…' : 'Afficher les suivants' }}\r\n </button>\r\n </ng-container>\r\n</div>\r\n",
20944
- styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;height:100%}.card-list{flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:8px;padding:10px;background:#f4f6fb}.card-item{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:12px;align-items:center;width:100%;padding:10px 12px;border:1px solid #DBE4FF;border-radius:4px 0 0 4px;background:#fff;color:#4d5a81;font:inherit;text-align:left;cursor:pointer}.card-item.no-image{grid-template-columns:minmax(0,1fr) auto}.card-item:hover{border-color:#5272d3}.card-item:focus-visible{outline:2px solid #5272D3;outline-offset:1px}.card-item.selected{border-color:#5272d3;box-shadow:inset 3px 0 #5272d3}.card-image{width:44px;height:44px;border-radius:4px;background:#ebf0ff;display:grid;place-items:center;overflow:hidden}.card-image img{max-width:100%;max-height:100%;object-fit:contain}.card-initials{color:#4465c9;font-weight:700;font-size:13px}.card-main{min-width:0;display:flex;flex-direction:column}.card-title,.card-text,.card-detail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-title{font-size:12px;color:#4465c9}.card-text{font-size:14px;font-weight:700}.card-detail{font-size:12px;color:#9fa9c7}.card-badge{padding:1px 8px;border-radius:999px;background:#ebf0ff;color:#4465c9;font-size:11px;font-weight:700;white-space:nowrap}.card-info{padding:16px;text-align:center;color:#9fa9c7}.card-more{align-self:center;margin:4px 0 8px;padding:6px 14px;border:1px solid #C1D0FD;border-radius:999px;background:#fff;color:#4465c9;font:inherit;cursor:pointer}.card-more:disabled{cursor:progress;opacity:.7}\n"]
21565
+ styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;height:100%}.card-list{flex:1 1 auto;min-height:0;overflow-y:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(300px,100%),1fr));align-content:start;gap:8px;padding:10px;background:#f4f6fb}.card-list>.card-info,.card-list>.card-more{grid-column:1/-1}.card-item{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:12px;align-items:center;width:100%;padding:10px 12px;border:1px solid #DBE4FF;border-radius:0 4px 4px 0;background:#fff;color:#4d5a81;font:inherit;text-align:left;cursor:pointer}.card-item.no-image{grid-template-columns:minmax(0,1fr) auto}.card-item:hover{border-color:#5272d3}.card-item:focus-visible{outline:2px solid #5272D3;outline-offset:1px}.card-item.selected{border-color:#5272d3;box-shadow:inset 3px 0 #5272d3}.card-image{width:44px;height:44px;border-radius:4px;background:#ebf0ff;display:grid;place-items:center;overflow:hidden}.card-image img{max-width:100%;max-height:100%;object-fit:contain}.card-initials{color:#4465c9;font-weight:700;font-size:13px}.card-main{min-width:0;display:flex;flex-direction:column}.card-title,.card-text,.card-detail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-title{font-size:12px;color:#4465c9}.card-text{font-size:14px;font-weight:700}.card-detail{font-size:12px;color:#9fa9c7}.card-badge{padding:1px 8px;border-radius:999px;background:#ebf0ff;color:#4465c9;font-size:11px;font-weight:700;white-space:nowrap}.card-info{padding:16px;text-align:center;color:#9fa9c7}.card-more{justify-self:center;margin:4px 0 8px;padding:6px 14px;border:1px solid #C1D0FD;border-radius:999px;background:#fff;color:#4465c9;font:inherit;cursor:pointer}.card-more:disabled{cursor:progress;opacity:.7}\n"]
20945
21566
  }]
20946
21567
  }],
20947
21568
  ctorParameters: () => [
@@ -24411,11 +25032,7 @@ var BaseComponent = class BaseComponent extends DflatBaseComponent {
24411
25032
  this.FILE_SECTION = "Fichiers";
24412
25033
  this.busMessages = inject(BusMessageService, { optional: true });
24413
25034
  this.ppService = inject(PP_SERVICE, { optional: true });
24414
- this.savedViewQueries = {
24415
- sortQuery: "",
24416
- filterQuery: ""
24417
- };
24418
- this.viewQueriesLoaded = false;
25035
+ this.panelFields = [];
24419
25036
  this.hostElement = inject(ElementRef, { optional: true });
24420
25037
  this.proposedColumnOrder = "";
24421
25038
  this.guid = Guid.create();
@@ -24443,7 +25060,6 @@ var BaseComponent = class BaseComponent extends DflatBaseComponent {
24443
25060
  this.registerSelectColumnsAction();
24444
25061
  this.subscribeFormChanged();
24445
25062
  this.subscribePresentationChanged();
24446
- this.watchLeaveForColumnOrder();
24447
25063
  }
24448
25064
  subscribeFormChanged() {
24449
25065
  this.busMessages?.of(FormChangedMessage).pipe(filter$1((message) => message.concerns(this.getEntityKey())), exhaustMap(() => this.confirmFormReload$()), takeUntil$1(this.destroy$)).subscribe(() => this.reloadForm());
@@ -24565,15 +25181,33 @@ var BaseComponent = class BaseComponent extends DflatBaseComponent {
24565
25181
  this.facade?.layout?.updateScreenWidth();
24566
25182
  }
24567
25183
  registerViewChooser(config) {
24568
- this.facade.config.viewChooser = {
24569
- loadViews: () => this.ppService.loadViews(config),
25184
+ combineLatest([this.facade.config.filterDescriptors$, this.facade.layout.hiddenSummaryFilters$]).pipe(takeUntil$1(this.destroy$)).subscribe(([fields, hidden]) => this.panelFields = (fields ?? []).filter((f) => !!f.PropertyName && f.PropertyName !== "Keywords" && f.PropertyName !== "Id" && !f.IsHidden && !hidden.includes(f.PropertyName)));
25185
+ this.facade.filter.viewFilterHandler = {
25186
+ save: () => {
25187
+ const order = this.facade.config.getDisplayedColumnKeys();
25188
+ this.ppService.saveViewFilter({
25189
+ ...config,
25190
+ title: this.getTitle()
25191
+ }, order, this.currentViewQueries()).pipe(take$1(1)).subscribe((saved) => {
25192
+ if (!saved) return;
25193
+ this.proposedColumnOrder = JSON.stringify(order);
25194
+ this.resetColumns();
25195
+ this.applyCurrentViewQueries(config);
25196
+ });
25197
+ },
25198
+ cancel: () => this.applyCurrentViewQueries(config)
25199
+ };
25200
+ this.facade.config.viewChooser = {
25201
+ loadViews: () => this.ppService.loadViews(config),
24570
25202
  select: (id) => this.ppService.selectView(config, id).pipe(take$1(1)).subscribe(() => {
24571
25203
  this.resetColumns();
24572
25204
  this.applyCurrentViewQueries(config);
24573
25205
  }),
24574
25206
  edit: (id) => this.openViewEditor(config, id, "columns"),
24575
- create: () => this.ppService.createView(config).pipe(take$1(1)).subscribe((created) => {
24576
- if (created) this.resetColumns();
25207
+ create: () => this.ppService.createView(config, this.currentViewQueries()).pipe(take$1(1)).subscribe((created) => {
25208
+ if (!created) return;
25209
+ this.resetColumns();
25210
+ this.applyCurrentViewQueries(config);
24577
25211
  }),
24578
25212
  loadColumns: () => this.ppService.loadViewColumns(config).pipe(map$1((columns) => {
24579
25213
  const shown = new Set(this.facade.config.getDisplayedColumnKeys());
@@ -24593,18 +25227,8 @@ var BaseComponent = class BaseComponent extends DflatBaseComponent {
24593
25227
  }), catchError$1(() => of(false)));
24594
25228
  },
24595
25229
  displayedColumns: () => this.facade.config.getDisplayedColumnKeys(),
24596
- loadCards: () => this.ppService.loadCardViews(config),
24597
- linkCard: (id) => this.ppService.linkCardView(config, id).pipe(take$1(1)).subscribe((linked) => {
24598
- if (linked) this.resetColumns();
24599
- }),
24600
- editCard: () => this.ppService.editCardView({
24601
- ...config,
24602
- title: this.getTitle()
24603
- }).pipe(take$1(1)).subscribe((saved) => {
24604
- if (saved) this.resetColumns();
24605
- }),
24606
25230
  moveColumn: (key, targetKey, before) => this.facade.config.moveDisplayedColumn(key, targetKey, before),
24607
- isModified: () => this.facade.config.isColumnLayoutModified() || this.viewQueriesModified(),
25231
+ isModified: () => this.facade.config.isColumnLayoutModified(),
24608
25232
  saveCurrent: () => {
24609
25233
  const order = this.unsavedLayout();
24610
25234
  if (!order) return;
@@ -24614,31 +25238,43 @@ var BaseComponent = class BaseComponent extends DflatBaseComponent {
24614
25238
  title: this.getTitle()
24615
25239
  }, order, queries).pipe(take$1(1)).subscribe((saved) => {
24616
25240
  if (!saved) return;
24617
- this.proposedColumnOrder = JSON.stringify({
24618
- order,
24619
- queries
24620
- });
24621
- this.savedViewQueries = queries;
25241
+ this.proposedColumnOrder = JSON.stringify(order);
24622
25242
  this.resetColumns();
24623
25243
  });
24624
- },
24625
- editFilters: () => this.openViewEditor(config, null, "filters")
25244
+ }
24626
25245
  };
24627
25246
  this.applyCurrentViewQueries(config, true);
24628
25247
  }
25248
+ get panelProperties() {
25249
+ return this.panelFields.map((f) => f.PropertyName);
25250
+ }
24629
25251
  currentViewQueries() {
24630
25252
  const sort = this.facade.filter.getSortList();
25253
+ const panel = this.facade.filter.getPanelValues(this.panelProperties);
25254
+ const filter = BaseComponent.bothFilters(this.facade.filter.getViewQuery(), this.facade.filter.getAdditionalQueryGroup()) ?? {
25255
+ Logic: "and",
25256
+ Conditions: []
25257
+ };
24631
25258
  return {
24632
25259
  sortQuery: sort.length ? JSON.stringify(sort) : "",
24633
- filterQuery: toViewQuery(this.facade.filter.getViewQuery()) ?? ""
25260
+ filterQuery: toViewQuery(panel ? {
25261
+ ...filter,
25262
+ Panel: panel
25263
+ } : filter) ?? ""
24634
25264
  };
24635
25265
  }
24636
- viewQueriesModified() {
24637
- if (!this.viewQueriesLoaded) return false;
24638
- const current = this.currentViewQueries();
24639
- const defaultSort = this.facade.filter.getDefaultSortList();
24640
- const savedSort = this.savedViewQueries.sortQuery || (defaultSort.length ? JSON.stringify(defaultSort) : "");
24641
- return current.sortQuery !== savedSort && current.sortQuery !== this.savedViewQueries.sortQuery || current.filterQuery !== this.savedViewQueries.filterQuery;
25266
+ static bothFilters(view, additional) {
25267
+ if (!view || !additional) return view ?? additional;
25268
+ if (view.Logic === "and" && additional.Logic === "and") return {
25269
+ ...view,
25270
+ Conditions: [...view.Conditions, ...additional.Conditions],
25271
+ Groups: [...view.Groups ?? [], ...additional.Groups ?? []]
25272
+ };
25273
+ return {
25274
+ Logic: "and",
25275
+ Conditions: [],
25276
+ Groups: [view, additional]
25277
+ };
24642
25278
  }
24643
25279
  applyCurrentViewQueries(config, initial = false) {
24644
25280
  this.ppService.loadViews(config).pipe(take$1(1)).subscribe((list) => {
@@ -24650,11 +25286,11 @@ var BaseComponent = class BaseComponent extends DflatBaseComponent {
24650
25286
  filterQuery: toViewQuery(filter) ?? ""
24651
25287
  };
24652
25288
  const current = this.currentViewQueries();
24653
- const keepCurrent = initial && (current.sortQuery !== "" || current.filterQuery !== "");
24654
- this.savedViewQueries = keepCurrent ? current : saved;
24655
- this.viewQueriesLoaded = true;
24656
- if (keepCurrent) return;
24657
- this.facade.filter.setViewQuery(filter);
25289
+ this.facade.filter.setViewFilter(view?.label ?? "", filter, !!view && (!view.isDefault || list.canEditDefault));
25290
+ if (initial && (current.sortQuery !== "" || current.filterQuery !== "")) return;
25291
+ this.facade.filter.clearAdditionalQuery();
25292
+ this.facade.filter.setViewQuery(withoutViewPanel(filter));
25293
+ this.facade.filter.setPanelValues(this.panelProperties, viewPanelOf(filter));
24658
25294
  if (saved.sortQuery !== current.sortQuery) this.facade.filter.setSortList(sort);
24659
25295
  });
24660
25296
  }
@@ -24676,9 +25312,61 @@ var BaseComponent = class BaseComponent extends DflatBaseComponent {
24676
25312
  ...config,
24677
25313
  viewId,
24678
25314
  tab,
24679
- filterColumns: (descriptors ?? []).filter((d) => !!d.Property).map((d) => BaseComponent.filterColumnOf(d))
25315
+ filterColumns: (descriptors ?? []).filter((d) => !!d.Property).map((d) => BaseComponent.filterColumnOf(d)),
25316
+ panelFields: this.panelFields.map((f) => BaseComponent.panelColumnOf(f)).filter((c) => !!c),
25317
+ gridQueries: this.currentViewQueries()
24680
25318
  }));
24681
25319
  }
25320
+ static panelColumnOf(f) {
25321
+ const key = f.PropertyName;
25322
+ const label = f.Label || key;
25323
+ switch (f.FilterType) {
25324
+ case FilterType.ComboBox:
25325
+ case FilterType.HorizComboBox:
25326
+ case FilterType.RadioButton: {
25327
+ const data = Array.isArray(f.OriginalData) ? f.OriginalData : Array.isArray(f.Data) ? f.Data : [];
25328
+ const valueMember = f.ValueMember || "Id";
25329
+ const displayMember = f.DisplayMember || "Label";
25330
+ return {
25331
+ key,
25332
+ label,
25333
+ type: "list",
25334
+ values: data.map((d) => ({
25335
+ value: String(d?.[valueMember]),
25336
+ label: String(d?.[displayMember] ?? d?.[valueMember])
25337
+ }))
25338
+ };
25339
+ }
25340
+ case FilterType.Integer:
25341
+ case FilterType.Decimal:
25342
+ case FilterType.WeekNumber: return {
25343
+ key,
25344
+ label,
25345
+ type: "number"
25346
+ };
25347
+ case FilterType.Date:
25348
+ case FilterType.DateTime: return {
25349
+ key,
25350
+ label,
25351
+ type: "date"
25352
+ };
25353
+ case FilterType.CheckBox: return {
25354
+ key,
25355
+ label,
25356
+ type: "bool"
25357
+ };
25358
+ case FilterType.String:
25359
+ case FilterType.Text:
25360
+ case FilterType.Email:
25361
+ case void 0:
25362
+ case null: return {
25363
+ key,
25364
+ label,
25365
+ type: "text"
25366
+ };
25367
+ default: return null;
25368
+ }
25369
+ }
24682
25370
  static filterColumnOf(d) {
24683
25371
  const type = d.Display?.type;
24684
25372
  const label = d.Name || d.FieldName || d.Property;
@@ -24714,37 +25402,36 @@ var BaseComponent = class BaseComponent extends DflatBaseComponent {
24714
25402
  }
24715
25403
  unsavedLayout() {
24716
25404
  if (!this.ppService || !this.IsTheMainComponent || !this.getPpMetaEntityKey()) return null;
24717
- if (!this.facade?.config?.isColumnLayoutModified() && !this.viewQueriesModified()) return null;
25405
+ if (!this.facade?.config?.isColumnLayoutModified()) return null;
24718
25406
  const columns = this.facade.config.getDisplayedColumnKeys();
24719
25407
  return columns.length > 0 ? columns : null;
24720
25408
  }
24721
25409
  unsavedColumnOrder() {
24722
25410
  const columns = this.unsavedLayout();
24723
- return columns && JSON.stringify({
24724
- order: columns,
24725
- queries: this.currentViewQueries()
24726
- }) !== this.proposedColumnOrder ? columns : null;
25411
+ return columns && JSON.stringify(columns) !== this.proposedColumnOrder ? columns : null;
24727
25412
  }
24728
25413
  saveColumnOrderOnLeave() {
24729
25414
  const order = this.unsavedColumnOrder();
24730
25415
  if (!order) return;
24731
25416
  const queries = this.currentViewQueries();
24732
- this.proposedColumnOrder = JSON.stringify({
24733
- order,
24734
- queries
24735
- });
25417
+ this.proposedColumnOrder = JSON.stringify(order);
24736
25418
  this.ppService.saveColumnOrder({
24737
25419
  ...this.getPpConfig(),
24738
25420
  title: this.getTitle()
24739
25421
  }, order, queries);
24740
25422
  }
24741
- watchLeaveForColumnOrder() {
24742
- const events = this.router?.events;
24743
- if (!this.IsTheMainComponent || !events) return;
24744
- events.pipe(filter$1((e) => e instanceof NavigationStart), takeUntil$1(this.destroy$)).subscribe((e) => {
24745
- const path = (url) => Utilities.splitInTwo(url, "?").firstPart;
24746
- if (this.isDisplayed() && path(e.url) !== path(this.router.url)) this.saveColumnOrderOnLeave();
24747
- });
25423
+ canLeaveView() {
25424
+ const order = this.unsavedLayout();
25425
+ if (!order || !this.isDisplayed()) return true;
25426
+ const queries = this.currentViewQueries();
25427
+ return this.ppService.confirmLeaveView({
25428
+ ...this.getPpConfig(),
25429
+ title: this.getTitle()
25430
+ }, order, queries).pipe(take$1(1), map$1((leave) => {
25431
+ this.proposedColumnOrder = "";
25432
+ this.resetColumns();
25433
+ return leave;
25434
+ }), catchError$1(() => of(true)));
24748
25435
  }
24749
25436
  isDisplayed() {
24750
25437
  return !!this.hostElement?.nativeElement?.isConnected;
@@ -24913,722 +25600,232 @@ var BaseEntityEditor = class BaseEntityEditor {
24913
25600
  ...values
24914
25601
  };
24915
25602
  updated["isValid"] = this.form.valid;
24916
- this.facade.change.emitUpdated([updated]);
24917
- }
24918
- setEntity(entity) {
24919
- this.entity = entity;
24920
- this.updateReadonly();
24921
- this.patchForm();
24922
- this.onEntityChanged(entity);
24923
- }
24924
- updateReadonly() {
24925
- this.readonly = this.facadeReadOnly || !Utilities.IsNil(this.entity?.["IsEditable"]) && !this.entity.IsEditable;
24926
- }
24927
- initForm() {
24928
- this.fields.forEach((field) => {
24929
- const validators = this.validationService.getFieldValidators(field);
24930
- const control = this.form.get(field.PropertyName);
24931
- if (!control) this.form.addControl(field.PropertyName, new FormControl(null, validators));
24932
- else {
24933
- control.setValidators(validators);
24934
- control.updateValueAndValidity({ emitEvent: false });
24935
- }
24936
- });
24937
- }
24938
- patchForm() {
24939
- this.form.reset(void 0, { emitEvent: false });
24940
- if (!Utilities.IsNil(this.entity)) this.form.patchValue(this.entity, { emitEvent: false });
24941
- }
24942
- static {
24943
- this.ɵfac = i0.ɵɵngDeclareFactory({
24944
- minVersion: "12.0.0",
24945
- version: "22.2.1",
24946
- ngImport: i0,
24947
- type: BaseEntityEditor,
24948
- deps: [{ token: ApplicationService }, { token: ValidationService }],
24949
- target: i0.ɵɵFactoryTarget.Directive
24950
- });
24951
- }
24952
- static {
24953
- this.ɵdir = i0.ɵɵngDeclareDirective({
24954
- minVersion: "14.0.0",
24955
- version: "22.2.1",
24956
- type: BaseEntityEditor,
24957
- isStandalone: true,
24958
- inputs: {
24959
- entityKey: "entityKey",
24960
- guid: "guid"
24961
- },
24962
- ngImport: i0
24963
- });
24964
- }
24965
- };
24966
- i0.ɵɵngDeclareClassMetadata({
24967
- minVersion: "12.0.0",
24968
- version: "22.2.1",
24969
- ngImport: i0,
24970
- type: BaseEntityEditor,
24971
- decorators: [{ type: Directive }],
24972
- ctorParameters: () => [{ type: ApplicationService }, { type: ValidationService }],
24973
- propDecorators: {
24974
- entityKey: [{ type: Input }],
24975
- guid: [{ type: Input }]
24976
- }
24977
- });
24978
- var SelectorRegistry = class SelectorRegistry {
24979
- constructor() {
24980
- this.registry = /* @__PURE__ */ new Map();
24981
- }
24982
- register(entityKey, config) {
24983
- this.registry.set(entityKey, config);
24984
- }
24985
- resolve(entityKey) {
24986
- const cfg = this.registry.get(entityKey);
24987
- if (!cfg) throw new Error(`No selector registered for ${entityKey}`);
24988
- return cfg;
24989
- }
24990
- static {
24991
- this.ɵfac = i0.ɵɵngDeclareFactory({
24992
- minVersion: "12.0.0",
24993
- version: "22.2.1",
24994
- ngImport: i0,
24995
- type: SelectorRegistry,
24996
- deps: [],
24997
- target: i0.ɵɵFactoryTarget.Injectable
24998
- });
24999
- }
25000
- static {
25001
- this.ɵprov = i0.ɵɵngDeclareInjectable({
25002
- minVersion: "12.0.0",
25003
- version: "22.2.1",
25004
- ngImport: i0,
25005
- type: SelectorRegistry
25006
- });
25007
- }
25008
- };
25009
- i0.ɵɵngDeclareClassMetadata({
25010
- minVersion: "12.0.0",
25011
- version: "22.2.1",
25012
- ngImport: i0,
25013
- type: SelectorRegistry,
25014
- decorators: [{ type: Injectable }]
25015
- });
25016
- var SelectorDataSource = class {
25017
- constructor(config) {
25018
- this.config = config;
25019
- this.cache = /* @__PURE__ */ new Map();
25020
- }
25021
- load(term, page) {
25022
- const key = `${term}|${page}`;
25023
- if (this.cache.has(key)) return of(this.cache.get(key));
25024
- return this.config.load({
25025
- term,
25026
- page,
25027
- pageSize: this.config.pageSize
25028
- }).pipe(tap((data) => this.cache.set(key, data)));
25029
- }
25030
- clearCache() {
25031
- this.cache.clear();
25032
- }
25033
- };
25034
- var AppConfigService = class AppConfigService {
25035
- constructor(app) {
25036
- this.app = app;
25037
- this.memoryCache = /* @__PURE__ */ new Map();
25038
- this.ttl = 3e5;
25039
- this.storagePrefix = "entityConfigCache:";
25040
- this.configSubjects = /* @__PURE__ */ new Map();
25041
- this.initializeFromLocalStorage();
25042
- }
25043
- initializeFromLocalStorage() {
25044
- try {
25045
- Object.keys(localStorage).filter((key) => key.startsWith(this.storagePrefix)).forEach((key) => {
25046
- try {
25047
- const storedItem = localStorage.getItem(key);
25048
- if (storedItem) {
25049
- const parsedItem = JSON.parse(storedItem);
25050
- if (Date.now() - parsedItem.timestamp < this.ttl) {
25051
- const config$ = of(parsedItem.config).pipe(shareReplay(1));
25052
- this.memoryCache.set(key.replace(this.storagePrefix, ""), {
25053
- observable: config$,
25054
- timestamp: parsedItem.timestamp
25055
- });
25056
- } else localStorage.removeItem(key);
25057
- }
25058
- } catch (innerError) {
25059
- console.error("Error parsing stored cache item:", innerError);
25060
- localStorage.removeItem(key);
25061
- }
25062
- });
25063
- } catch (error) {
25064
- console.error("Error initializing cache from localStorage:", error);
25065
- }
25066
- }
25067
- loadConfig(entityKey) {
25068
- return combineLatest({
25069
- columns: this.app.DataService.getViewColumns(entityKey).pipe(startWith(null)),
25070
- viewModel: this.app.DataService.getEntityViewModel(entityKey, 0).pipe(startWith(null)),
25071
- emptyFilter: this.app.DataService.getEmptyFilter(entityKey).pipe(startWith(null)),
25072
- filterDescriptors: this.app.DataService.getViewFilterDescriptors(entityKey).pipe(startWith(null)),
25073
- actions: this.app.DataService.getExtendedActions(entityKey).pipe(startWith(null))
25074
- });
25075
- }
25076
- saveToLocalStorage(entityKey, config) {
25077
- try {
25078
- const cacheEntry = this.memoryCache.get(entityKey);
25079
- if (!cacheEntry) return;
25080
- const storableItem = {
25081
- config,
25082
- timestamp: cacheEntry.timestamp
25083
- };
25084
- localStorage.setItem(`${this.storagePrefix}${entityKey}`, JSON.stringify(storableItem));
25085
- } catch (error) {
25086
- console.error("Error saving to localStorage:", error);
25087
- }
25088
- }
25089
- resetConfig(entityKey) {
25090
- if (entityKey) {
25091
- this.memoryCache.delete(entityKey);
25092
- localStorage.removeItem(`${this.storagePrefix}${entityKey}`);
25093
- } else {
25094
- this.memoryCache.clear();
25095
- Object.keys(localStorage).filter((k) => k.startsWith(this.storagePrefix)).forEach((k) => localStorage.removeItem(k));
25096
- }
25097
- }
25098
- getAllConfigs() {
25099
- const now = Date.now();
25100
- const validConfigs = /* @__PURE__ */ new Map();
25101
- for (const [key, entry] of this.memoryCache.entries()) if (now - entry.timestamp < this.ttl) validConfigs.set(key, entry.observable);
25102
- return validConfigs;
25103
- }
25104
- getConfig$(entityKey) {
25105
- if (!this.configSubjects.has(entityKey)) {
25106
- const subject = new BehaviorSubject(null);
25107
- this.configSubjects.set(entityKey, subject);
25108
- this.loadConfig(entityKey).subscribe({
25109
- next: (cfg) => subject.next(cfg),
25110
- error: (err) => {
25111
- console.error(`Error loading config for ${entityKey}`, err);
25112
- subject.error(err);
25113
- }
25114
- });
25115
- }
25116
- return this.configSubjects.get(entityKey).pipe(filter((cfg) => !!cfg), distinctUntilChanged((a, b) => isEqual(a, b)), shareReplay({
25117
- refCount: true,
25118
- bufferSize: 1
25119
- }));
25120
- }
25121
- reloadConfig(entityKey) {
25122
- const subject = this.configSubjects.get(entityKey);
25123
- if (subject) this.loadConfig(entityKey).subscribe((cfg) => subject.next(cfg));
25124
- }
25125
- static {
25126
- this.ɵfac = i0.ɵɵngDeclareFactory({
25127
- minVersion: "12.0.0",
25128
- version: "22.2.1",
25129
- ngImport: i0,
25130
- type: AppConfigService,
25131
- deps: [{ token: ApplicationService }],
25132
- target: i0.ɵɵFactoryTarget.Injectable
25133
- });
25134
- }
25135
- static {
25136
- this.ɵprov = i0.ɵɵngDeclareInjectable({
25137
- minVersion: "12.0.0",
25138
- version: "22.2.1",
25139
- ngImport: i0,
25140
- type: AppConfigService,
25141
- providedIn: "root"
25142
- });
25143
- }
25144
- };
25145
- i0.ɵɵngDeclareClassMetadata({
25146
- minVersion: "12.0.0",
25147
- version: "22.2.1",
25148
- ngImport: i0,
25149
- type: AppConfigService,
25150
- decorators: [{
25151
- type: Injectable,
25152
- args: [{ providedIn: "root" }]
25153
- }],
25154
- ctorParameters: () => [{ type: ApplicationService }]
25155
- });
25156
- var BaseFormContext = class {
25157
- constructor(fb, validationService) {
25158
- this.fb = fb;
25159
- this.validationService = validationService;
25160
- this.fields = [];
25161
- this.trackByField = (_, d) => d.PropertyName;
25162
- }
25163
- initPropertyForm(withValidators = false) {
25164
- if (!this.fields) return;
25165
- if (!this.form) this.form = new FormGroup({});
25166
- else this.form.reset();
25167
- this.fields.forEach((field) => {
25168
- const validators = withValidators ? this.validationService.getFieldValidators(field) : [];
25169
- const controlValue = this.entity?.hasOwnProperty(field.PropertyName) && this.entity[field.PropertyName] !== void 0 ? this.entity[field.PropertyName] : null;
25170
- if (!this.form.contains(field.PropertyName)) this.form.addControl(field.PropertyName, new FormControl(controlValue, validators));
25171
- });
25172
- if (this.entity) this.form.patchValue(this.entity);
25173
- }
25174
- triggerField(desc) {
25175
- if (desc.IsConditioning) this.handleConditional(desc);
25176
- if (desc.CascadeDependency) this.handleCascade(desc);
25177
- }
25178
- setupCascade(descriptors) {
25179
- descriptors.forEach((desc) => {
25180
- if (!desc.CascadeDependency || !desc.CascadeDependencyProperty) return;
25181
- this.form.get(desc.PropertyName)?.valueChanges.subscribe(() => this.handleCascade(desc));
25182
- });
25183
- }
25184
- setupConditional(descriptors) {
25185
- descriptors.forEach((desc) => {
25186
- if (!desc.IsConditioning) return;
25187
- this.form.get(desc.PropertyName)?.valueChanges.subscribe(() => this.handleConditional(desc));
25188
- });
25189
- }
25190
- changes$() {
25191
- return this.form.valueChanges.pipe(debounceTime$1(50), distinctUntilChanged$1(), shareReplay$1(1));
25192
- }
25193
- patch(values) {
25194
- this.form.patchValue(values, { emitEvent: true });
25195
- }
25196
- serialize() {
25197
- return this.form.getRawValue();
25198
- }
25199
- handleCascade(field) {
25200
- const selectedValue = field.FilterType === FilterType.Integer ? Number(this.entity[field.PropertyName]) : this.entity[field.PropertyName];
25201
- const props = field.CascadeDependencyProperty.split(";");
25202
- const dependentFields = this.fields.filter((f) => props.includes(f.PropertyName));
25203
- for (const dependingField of dependentFields) {
25204
- dependingField.CurrentFilters = dependingField.CurrentFilters || {};
25205
- if (Utilities.IsNil(selectedValue) || Utilities.isEmptyString(selectedValue)) delete dependingField.CurrentFilters[field.PropertyName];
25206
- else dependingField.CurrentFilters[field.PropertyName] = true;
25207
- this.resetFieldData(dependingField);
25208
- if (field.FilterDistinct) dependingField.Data = [...new Map(dependingField.Data.map((item) => [item[dependingField.PropertyName], item])).values()];
25209
- }
25210
- }
25211
- handleConditional(field) {
25212
- const allFields = this.fields.filter((f) => f.ConditioningField === field.PropertyName);
25213
- const conditioningValue = this.form.get(field.PropertyName)?.value;
25214
- for (let conditionedField of allFields) {
25215
- const conditionMet = Utilities.IsNil(conditioningValue) || conditionedField.ConditioningFieldValue.includes(conditioningValue) || conditionedField.BoolConditioningFieldValue === conditioningValue;
25216
- if (conditionedField.ConditionalResult === ConditionalResults.Hide) conditionedField.IsHidden = conditionMet;
25217
- if (conditionedField.ConditionalResult === ConditionalResults.ReadOnly) conditionedField.ReadOnly = conditionMet;
25218
- const control = this.form.get(conditionedField.PropertyName);
25219
- if (control) {
25220
- if (conditionedField.IsHidden) control.clearValidators();
25221
- else {
25222
- const validators = this.validationService.getValidators(conditionedField.FieldValidators);
25223
- this.updateControlValidators(control, validators);
25224
- }
25225
- }
25226
- }
25227
- }
25228
- resetFieldData(field) {
25229
- if (Utilities.IsNil(field.OriginalData)) return;
25230
- if (field.FilterType === FilterType.Integer) field.Data = field.OriginalData.filter((p) => p[field.ValueMember] === -1 || Object.keys(field.CurrentFilters).every((name) => p[name] === this.entity[name]));
25231
- else field.Data = field.OriginalData.filter((p) => Object.keys(field.CurrentFilters).every((name) => p[name] === this.entity[name]));
25232
- }
25233
- updateControlValidators(ctrl, validators) {
25234
- ctrl.setValidators(validators);
25235
- ctrl.updateValueAndValidity({ emitEvent: false });
25236
- }
25237
- };
25238
- var SortingState = class {
25239
- constructor(entityKey) {
25240
- this.entityKey = entityKey;
25241
- this.property$ = new BehaviorSubject(" ");
25242
- this.direction$ = new BehaviorSubject("desc");
25243
- }
25244
- get changes$() {
25245
- return this.property$.pipe(distinctUntilChanged(), (source) => new Observable((observer) => {
25246
- const sub1 = this.property$.subscribe(() => observer.next(this.snapshot()));
25247
- const sub2 = this.direction$.subscribe(() => observer.next(this.snapshot()));
25248
- return () => {
25249
- sub1.unsubscribe();
25250
- sub2.unsubscribe();
25251
- };
25252
- }));
25253
- }
25254
- snapshot() {
25255
- return {
25256
- property: this.property$.value,
25257
- direction: this.direction$.value
25258
- };
25259
- }
25260
- updateProperty(property) {
25261
- if (this.property$.value === property) this.toggleDirection();
25262
- else {
25263
- this.property$.next(property);
25264
- this.direction$.next("asc");
25265
- }
25266
- }
25267
- toggleDirection() {
25268
- const next = this.direction$.value === "asc" ? "desc" : "asc";
25269
- this.direction$.next(next);
25270
- }
25271
- reset() {
25272
- this.property$.next("");
25273
- this.direction$.next("asc");
25274
- }
25275
- };
25276
- const FilterOperators = {
25277
- text: [
25278
- {
25279
- value: "contains",
25280
- label: "contient"
25281
- },
25282
- {
25283
- value: "notcontains",
25284
- label: "ne contient pas"
25285
- },
25286
- {
25287
- value: "startswith",
25288
- label: "commence par"
25289
- },
25290
- {
25291
- value: "endswith",
25292
- label: "finit par"
25293
- },
25294
- {
25295
- value: "eq",
25296
- label: "est égal à"
25297
- },
25298
- {
25299
- value: "ne",
25300
- label: "est différent de"
25301
- },
25302
- {
25303
- value: "isnull",
25304
- label: "est vide"
25305
- },
25306
- {
25307
- value: "notnull",
25308
- label: "n'est pas vide"
25309
- }
25310
- ],
25311
- number: [
25312
- {
25313
- value: "eq",
25314
- label: "="
25315
- },
25316
- {
25317
- value: "ne",
25318
- label: "≠"
25319
- },
25320
- {
25321
- value: "gt",
25322
- label: ">"
25323
- },
25324
- {
25325
- value: "ge",
25326
- label: "≥"
25327
- },
25328
- {
25329
- value: "lt",
25330
- label: "<"
25331
- },
25332
- {
25333
- value: "le",
25334
- label: "≤"
25335
- },
25336
- {
25337
- value: "between",
25338
- label: "entre"
25339
- },
25340
- {
25341
- value: "isnull",
25342
- label: "est vide"
25343
- },
25344
- {
25345
- value: "notnull",
25346
- label: "n'est pas vide"
25347
- }
25348
- ],
25349
- date: [
25350
- {
25351
- value: "on",
25352
- label: "le"
25353
- },
25354
- {
25355
- value: "before",
25356
- label: "avant le"
25357
- },
25358
- {
25359
- value: "after",
25360
- label: "après le"
25361
- },
25362
- {
25363
- value: "datebetween",
25364
- label: "entre"
25365
- },
25366
- {
25367
- value: "today",
25368
- label: "aujourd'hui"
25369
- },
25370
- {
25371
- value: "thismonth",
25372
- label: "ce mois-ci"
25373
- },
25374
- {
25375
- value: "isnull",
25376
- label: "est vide"
25377
- },
25378
- {
25379
- value: "notnull",
25380
- label: "n'est pas vide"
25381
- }
25382
- ],
25383
- bool: [{
25384
- value: "true",
25385
- label: "est oui"
25386
- }, {
25387
- value: "false",
25388
- label: "est non"
25389
- }],
25390
- list: [{
25391
- value: "in",
25392
- label: "est l'une de"
25393
- }, {
25394
- value: "notin",
25395
- label: "n'est aucune de"
25396
- }]
25397
- };
25398
- var FilterEditorComponent = class FilterEditorComponent {
25399
- constructor() {
25400
- this.columns = [];
25401
- this.filter = {
25402
- Logic: "and",
25403
- Conditions: [],
25404
- Groups: []
25405
- };
25406
- this.sort = [];
25407
- this.embedded = false;
25408
- this.section = null;
25409
- this.viewLabel = "";
25410
- this.canSaveInView = true;
25411
- this.saveInView = false;
25412
- this.tab = "filters";
25413
- this.maxDepth = 3;
25414
- this.trackIndex = (index) => index;
25603
+ this.facade.change.emitUpdated([updated]);
25415
25604
  }
25416
- get shownTab() {
25417
- return this.section ?? this.tab;
25605
+ setEntity(entity) {
25606
+ this.entity = entity;
25607
+ this.updateReadonly();
25608
+ this.patchForm();
25609
+ this.onEntityChanged(entity);
25418
25610
  }
25419
- get result() {
25420
- return {
25421
- filter: this.filter,
25422
- sort: this.sort.filter((s) => !!s.property),
25423
- saveInView: this.saveInView && this.canSaveInView
25424
- };
25611
+ updateReadonly() {
25612
+ this.readonly = this.facadeReadOnly || !Utilities.IsNil(this.entity?.["IsEditable"]) && !this.entity.IsEditable;
25425
25613
  }
25426
- columnOf(key) {
25427
- return this.columns.find((c) => c.key === key);
25614
+ initForm() {
25615
+ this.fields.forEach((field) => {
25616
+ const validators = this.validationService.getFieldValidators(field);
25617
+ const control = this.form.get(field.PropertyName);
25618
+ if (!control) this.form.addControl(field.PropertyName, new FormControl(null, validators));
25619
+ else {
25620
+ control.setValidators(validators);
25621
+ control.updateValueAndValidity({ emitEvent: false });
25622
+ }
25623
+ });
25428
25624
  }
25429
- operatorsOf(condition) {
25430
- return FilterOperators[this.columnOf(condition.Property)?.type ?? "text"];
25625
+ patchForm() {
25626
+ this.form.reset(void 0, { emitEvent: false });
25627
+ if (!Utilities.IsNil(this.entity)) this.form.patchValue(this.entity, { emitEvent: false });
25431
25628
  }
25432
- hasValue(condition) {
25433
- return !ViewFilterNoValueOperators.includes(condition.Operator);
25629
+ static {
25630
+ this.ɵfac = i0.ɵɵngDeclareFactory({
25631
+ minVersion: "12.0.0",
25632
+ version: "22.2.1",
25633
+ ngImport: i0,
25634
+ type: BaseEntityEditor,
25635
+ deps: [{ token: ApplicationService }, { token: ValidationService }],
25636
+ target: i0.ɵɵFactoryTarget.Directive
25637
+ });
25434
25638
  }
25435
- hasSecondValue(condition) {
25436
- return condition.Operator === "between" || condition.Operator === "datebetween";
25639
+ static {
25640
+ this.ɵdir = i0.ɵɵngDeclareDirective({
25641
+ minVersion: "14.0.0",
25642
+ version: "22.2.1",
25643
+ type: BaseEntityEditor,
25644
+ isStandalone: true,
25645
+ inputs: {
25646
+ entityKey: "entityKey",
25647
+ guid: "guid"
25648
+ },
25649
+ ngImport: i0
25650
+ });
25437
25651
  }
25438
- isList(condition) {
25439
- return condition.Operator === "in" || condition.Operator === "notin";
25652
+ };
25653
+ i0.ɵɵngDeclareClassMetadata({
25654
+ minVersion: "12.0.0",
25655
+ version: "22.2.1",
25656
+ ngImport: i0,
25657
+ type: BaseEntityEditor,
25658
+ decorators: [{ type: Directive }],
25659
+ ctorParameters: () => [{ type: ApplicationService }, { type: ValidationService }],
25660
+ propDecorators: {
25661
+ entityKey: [{ type: Input }],
25662
+ guid: [{ type: Input }]
25440
25663
  }
25441
- inputType(condition) {
25442
- const type = this.columnOf(condition.Property)?.type;
25443
- return type === "date" ? "date" : type === "number" ? "number" : "text";
25664
+ });
25665
+ var SelectorRegistry = class SelectorRegistry {
25666
+ constructor() {
25667
+ this.registry = /* @__PURE__ */ new Map();
25444
25668
  }
25445
- onColumnChange(condition) {
25446
- condition.Operator = this.operatorsOf(condition)[0].value;
25447
- condition.Value = null;
25448
- condition.Value2 = null;
25449
- condition.Values = this.isList(condition) ? [] : void 0;
25669
+ register(entityKey, config) {
25670
+ this.registry.set(entityKey, config);
25450
25671
  }
25451
- onOperatorChange(condition) {
25452
- if (this.isList(condition)) condition.Values = condition.Values ?? [];
25672
+ resolve(entityKey) {
25673
+ const cfg = this.registry.get(entityKey);
25674
+ if (!cfg) throw new Error(`No selector registered for ${entityKey}`);
25675
+ return cfg;
25453
25676
  }
25454
- toggleValue(condition, value) {
25455
- const values = condition.Values ?? [];
25456
- condition.Values = values.includes(value) ? values.filter((v) => v !== value) : [...values, value];
25677
+ static {
25678
+ this.ɵfac = i0.ɵɵngDeclareFactory({
25679
+ minVersion: "12.0.0",
25680
+ version: "22.2.1",
25681
+ ngImport: i0,
25682
+ type: SelectorRegistry,
25683
+ deps: [],
25684
+ target: i0.ɵɵFactoryTarget.Injectable
25685
+ });
25457
25686
  }
25458
- setLogic(group, logic) {
25459
- group.Logic = logic;
25687
+ static {
25688
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
25689
+ minVersion: "12.0.0",
25690
+ version: "22.2.1",
25691
+ ngImport: i0,
25692
+ type: SelectorRegistry
25693
+ });
25460
25694
  }
25461
- addCondition(group) {
25462
- const column = this.columns[0];
25463
- if (!column) return;
25464
- const condition = {
25465
- Property: column.key,
25466
- Operator: FilterOperators[column.type][0].value,
25467
- Value: null
25468
- };
25469
- if (this.isList(condition)) condition.Values = [];
25470
- group.Conditions.push(condition);
25695
+ };
25696
+ i0.ɵɵngDeclareClassMetadata({
25697
+ minVersion: "12.0.0",
25698
+ version: "22.2.1",
25699
+ ngImport: i0,
25700
+ type: SelectorRegistry,
25701
+ decorators: [{ type: Injectable }]
25702
+ });
25703
+ var SelectorDataSource = class {
25704
+ constructor(config) {
25705
+ this.config = config;
25706
+ this.cache = /* @__PURE__ */ new Map();
25471
25707
  }
25472
- removeCondition(group, index) {
25473
- group.Conditions.splice(index, 1);
25708
+ load(term, page) {
25709
+ const key = `${term}|${page}`;
25710
+ if (this.cache.has(key)) return of(this.cache.get(key));
25711
+ return this.config.load({
25712
+ term,
25713
+ page,
25714
+ pageSize: this.config.pageSize
25715
+ }).pipe(tap((data) => this.cache.set(key, data)));
25474
25716
  }
25475
- addGroup(group) {
25476
- const sub = {
25477
- Logic: group.Logic === "and" ? "or" : "and",
25478
- Conditions: [],
25479
- Groups: []
25480
- };
25481
- this.addCondition(sub);
25482
- group.Groups = [...group.Groups ?? [], sub];
25717
+ clearCache() {
25718
+ this.cache.clear();
25483
25719
  }
25484
- removeGroup(parent, index) {
25485
- parent.Groups?.splice(index, 1);
25720
+ };
25721
+ var AppConfigService = class AppConfigService {
25722
+ constructor(app) {
25723
+ this.app = app;
25724
+ this.memoryCache = /* @__PURE__ */ new Map();
25725
+ this.ttl = 3e5;
25726
+ this.storagePrefix = "entityConfigCache:";
25727
+ this.configSubjects = /* @__PURE__ */ new Map();
25728
+ this.initializeFromLocalStorage();
25486
25729
  }
25487
- clear() {
25488
- this.filter.Logic = "and";
25489
- this.filter.Conditions.splice(0);
25490
- this.filter.Groups = [];
25491
- this.sort.splice(0);
25730
+ initializeFromLocalStorage() {
25731
+ try {
25732
+ Object.keys(localStorage).filter((key) => key.startsWith(this.storagePrefix)).forEach((key) => {
25733
+ try {
25734
+ const storedItem = localStorage.getItem(key);
25735
+ if (storedItem) {
25736
+ const parsedItem = JSON.parse(storedItem);
25737
+ if (Date.now() - parsedItem.timestamp < this.ttl) {
25738
+ const config$ = of(parsedItem.config).pipe(shareReplay(1));
25739
+ this.memoryCache.set(key.replace(this.storagePrefix, ""), {
25740
+ observable: config$,
25741
+ timestamp: parsedItem.timestamp
25742
+ });
25743
+ } else localStorage.removeItem(key);
25744
+ }
25745
+ } catch (innerError) {
25746
+ console.error("Error parsing stored cache item:", innerError);
25747
+ localStorage.removeItem(key);
25748
+ }
25749
+ });
25750
+ } catch (error) {
25751
+ console.error("Error initializing cache from localStorage:", error);
25752
+ }
25492
25753
  }
25493
- addSort() {
25494
- const used = new Set(this.sort.map((s) => s.property));
25495
- const next = this.columns.find((c) => !used.has(c.key));
25496
- if (next) this.sort.push({
25497
- property: next.key,
25498
- direction: "asc"
25754
+ loadConfig(entityKey) {
25755
+ return combineLatest({
25756
+ columns: this.app.DataService.getViewColumns(entityKey).pipe(startWith(null)),
25757
+ viewModel: this.app.DataService.getEntityViewModel(entityKey, 0).pipe(startWith(null)),
25758
+ emptyFilter: this.app.DataService.getEmptyFilter(entityKey).pipe(startWith(null)),
25759
+ filterDescriptors: this.app.DataService.getViewFilterDescriptors(entityKey).pipe(startWith(null)),
25760
+ actions: this.app.DataService.getExtendedActions(entityKey).pipe(startWith(null))
25499
25761
  });
25500
25762
  }
25501
- removeSort(index) {
25502
- this.sort.splice(index, 1);
25763
+ saveToLocalStorage(entityKey, config) {
25764
+ try {
25765
+ const cacheEntry = this.memoryCache.get(entityKey);
25766
+ if (!cacheEntry) return;
25767
+ const storableItem = {
25768
+ config,
25769
+ timestamp: cacheEntry.timestamp
25770
+ };
25771
+ localStorage.setItem(`${this.storagePrefix}${entityKey}`, JSON.stringify(storableItem));
25772
+ } catch (error) {
25773
+ console.error("Error saving to localStorage:", error);
25774
+ }
25503
25775
  }
25504
- moveSort(index, offset) {
25505
- const target = index + offset;
25506
- if (target < 0 || target >= this.sort.length) return;
25507
- const [level] = this.sort.splice(index, 1);
25508
- this.sort.splice(target, 0, level);
25776
+ resetConfig(entityKey) {
25777
+ if (entityKey) {
25778
+ this.memoryCache.delete(entityKey);
25779
+ localStorage.removeItem(`${this.storagePrefix}${entityKey}`);
25780
+ } else {
25781
+ this.memoryCache.clear();
25782
+ Object.keys(localStorage).filter((k) => k.startsWith(this.storagePrefix)).forEach((k) => localStorage.removeItem(k));
25783
+ }
25784
+ }
25785
+ getAllConfigs() {
25786
+ const now = Date.now();
25787
+ const validConfigs = /* @__PURE__ */ new Map();
25788
+ for (const [key, entry] of this.memoryCache.entries()) if (now - entry.timestamp < this.ttl) validConfigs.set(key, entry.observable);
25789
+ return validConfigs;
25790
+ }
25791
+ getConfig$(entityKey) {
25792
+ if (!this.configSubjects.has(entityKey)) {
25793
+ const subject = new BehaviorSubject(null);
25794
+ this.configSubjects.set(entityKey, subject);
25795
+ this.loadConfig(entityKey).subscribe({
25796
+ next: (cfg) => subject.next(cfg),
25797
+ error: (err) => {
25798
+ console.error(`Error loading config for ${entityKey}`, err);
25799
+ subject.error(err);
25800
+ }
25801
+ });
25802
+ }
25803
+ return this.configSubjects.get(entityKey).pipe(filter((cfg) => !!cfg), distinctUntilChanged((a, b) => isEqual(a, b)), shareReplay({
25804
+ refCount: true,
25805
+ bufferSize: 1
25806
+ }));
25509
25807
  }
25510
- summary(group = this.filter) {
25511
- return [...group.Conditions.map((c) => {
25512
- const column = this.columnOf(c.Property);
25513
- const operator = FilterOperators[column?.type ?? "text"].find((o) => o.value === c.Operator)?.label ?? c.Operator;
25514
- const values = this.isList(c) ? (c.Values ?? []).map((v) => column?.values?.find((x) => x.value === v)?.label ?? v).join(", ") : !this.hasValue(c) ? "" : this.hasSecondValue(c) ? `${c.Value ?? "…"} et ${c.Value2 ?? "…"}` : c.Value ?? "…";
25515
- return `${column?.label ?? c.Property} ${operator}${values ? " « " + values + " »" : ""}`;
25516
- }), ...(group.Groups ?? []).map((g) => `(${this.summary(g)})`)].join(group.Logic === "or" ? " OU " : " ET ") || "Aucun filtre";
25808
+ reloadConfig(entityKey) {
25809
+ const subject = this.configSubjects.get(entityKey);
25810
+ if (subject) this.loadConfig(entityKey).subscribe((cfg) => subject.next(cfg));
25517
25811
  }
25518
25812
  static {
25519
25813
  this.ɵfac = i0.ɵɵngDeclareFactory({
25520
25814
  minVersion: "12.0.0",
25521
25815
  version: "22.2.1",
25522
25816
  ngImport: i0,
25523
- type: FilterEditorComponent,
25524
- deps: [],
25525
- target: i0.ɵɵFactoryTarget.Component
25817
+ type: AppConfigService,
25818
+ deps: [{ token: ApplicationService }],
25819
+ target: i0.ɵɵFactoryTarget.Injectable
25526
25820
  });
25527
25821
  }
25528
25822
  static {
25529
- this.ɵcmp = i0.ɵɵngDeclareComponent({
25530
- minVersion: "14.0.0",
25823
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
25824
+ minVersion: "12.0.0",
25531
25825
  version: "22.2.1",
25532
- type: FilterEditorComponent,
25533
- isStandalone: true,
25534
- selector: "alg-filter-editor",
25535
- inputs: {
25536
- columns: "columns",
25537
- filter: "filter",
25538
- sort: "sort",
25539
- embedded: "embedded",
25540
- section: "section"
25541
- },
25542
25826
  ngImport: i0,
25543
- template: "<div class=\"fe\">\r\n <div class=\"fe-tabs\" role=\"tablist\" *ngIf=\"!section\">\r\n <button type=\"button\" class=\"fe-tab\" [class.active]=\"tab === 'filters'\" (click)=\"tab = 'filters'\">Filtres</button>\r\n <button type=\"button\" class=\"fe-tab\" [class.active]=\"tab === 'sort'\" (click)=\"tab = 'sort'\">Tri</button>\r\n </div>\r\n\r\n <div class=\"fe-body\" *ngIf=\"shownTab === 'filters'\">\r\n <ng-container *ngTemplateOutlet=\"groupTpl; context: { group: filter, parent: null, index: 0, depth: 0 }\"></ng-container>\r\n <div class=\"fe-summary\">{{ summary() }}</div>\r\n </div>\r\n\r\n <div class=\"fe-body\" *ngIf=\"shownTab === 'sort'\">\r\n <div class=\"fe-sort\" *ngFor=\"let level of sort; let i = index; trackBy: trackIndex\">\r\n <span class=\"fe-sort-label\">{{ i === 0 ? 'Trier par' : 'Puis par' }}</span>\r\n <select class=\"form-control\" [(ngModel)]=\"level.property\">\r\n <option *ngFor=\"let column of columns\" [value]=\"column.key\">{{ column.label }}</option>\r\n </select>\r\n <select class=\"form-control fe-sort-direction\" [(ngModel)]=\"level.direction\">\r\n <option value=\"asc\">Croissant</option>\r\n <option value=\"desc\">Décroissant</option>\r\n </select>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Monter\" [disabled]=\"i === 0\" (click)=\"moveSort(i, -1)\"><i class=\"fa fa-arrow-up\"></i></button>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Descendre\" [disabled]=\"i === sort.length - 1\" (click)=\"moveSort(i, 1)\"><i class=\"fa fa-arrow-down\"></i></button>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer\" (click)=\"removeSort(i)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n <div class=\"fe-adds\"><button type=\"button\" class=\"fe-add\" (click)=\"addSort()\">+ Niveau</button></div>\r\n </div>\r\n\r\n <div class=\"fe-foot\">\r\n <label class=\"fe-save\" *ngIf=\"!embedded\" [class.disabled]=\"!canSaveInView\"\r\n [title]=\"canSaveInView ? '' : 'La vue par défaut est en lecture seule : dupliquez-la pour y enregistrer un filtre'\">\r\n <input type=\"checkbox\" [(ngModel)]=\"saveInView\" [disabled]=\"!canSaveInView\" />\r\n <span>Enregistrer dans la vue{{ viewLabel ? ' « ' + viewLabel + ' »' : '' }}</span>\r\n </label>\r\n <button type=\"button\" class=\"fe-clear\" (click)=\"clear()\">Tout effacer</button>\r\n </div>\r\n</div>\r\n\r\n<!-- Groupe : relation ET / OU, conditions, groupes imbriqués. -->\r\n<ng-template #groupTpl let-group=\"group\" let-parent=\"parent\" let-index=\"index\" let-depth=\"depth\">\r\n <div class=\"fe-group\" [class.or]=\"group.Logic === 'or'\" [class.nested]=\"depth > 0\">\r\n <div class=\"fe-group-head\">\r\n <span class=\"fe-logic\">\r\n <button type=\"button\" [class.on]=\"group.Logic === 'and'\" class=\"and\" (click)=\"setLogic(group, 'and')\">ET</button>\r\n <button type=\"button\" [class.on]=\"group.Logic === 'or'\" class=\"or\" (click)=\"setLogic(group, 'or')\">OU</button>\r\n </span>\r\n <span class=\"fe-group-hint\">{{ group.Logic === 'and' ? 'Toutes les conditions' : 'Au moins une condition' }}</span>\r\n <button type=\"button\" *ngIf=\"parent\" class=\"fe-icon fe-group-remove\" title=\"Retirer le groupe\" (click)=\"removeGroup(parent, index)\">\r\n <i class=\"fa fa-times\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"fe-cond\" *ngFor=\"let condition of group.Conditions; let i = index; trackBy: trackIndex\">\r\n <select class=\"form-control\" [(ngModel)]=\"condition.Property\" (ngModelChange)=\"onColumnChange(condition)\">\r\n <option *ngFor=\"let column of columns\" [value]=\"column.key\">{{ column.label }}</option>\r\n </select>\r\n <select class=\"form-control\" [(ngModel)]=\"condition.Operator\" (ngModelChange)=\"onOperatorChange(condition)\">\r\n <option *ngFor=\"let op of operatorsOf(condition)\" [value]=\"op.value\">{{ op.label }}</option>\r\n </select>\r\n <span class=\"fe-value\">\r\n <ng-container *ngIf=\"isList(condition); else valueTpl\">\r\n <span class=\"fe-chips\">\r\n <button type=\"button\" *ngFor=\"let option of columnOf(condition.Property)?.values\" class=\"fe-chip\"\r\n [class.on]=\"condition.Values?.includes(option.value)\" (click)=\"toggleValue(condition, option.value)\">{{ option.label }}</button>\r\n </span>\r\n </ng-container>\r\n <ng-template #valueTpl>\r\n <ng-container *ngIf=\"hasValue(condition)\">\r\n <input class=\"form-control\" [type]=\"inputType(condition)\" [(ngModel)]=\"condition.Value\" />\r\n <input *ngIf=\"hasSecondValue(condition)\" class=\"form-control\" [type]=\"inputType(condition)\" [(ngModel)]=\"condition.Value2\" />\r\n </ng-container>\r\n </ng-template>\r\n </span>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer la condition\" (click)=\"removeCondition(group, i)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n\r\n <ng-container *ngFor=\"let sub of group.Groups; let gi = index; trackBy: trackIndex\">\r\n <ng-container *ngTemplateOutlet=\"groupTpl; context: { group: sub, parent: group, index: gi, depth: depth + 1 }\"></ng-container>\r\n </ng-container>\r\n\r\n <div class=\"fe-adds\">\r\n <button type=\"button\" class=\"fe-add\" (click)=\"addCondition(group)\">+ Condition</button>\r\n <button type=\"button\" class=\"fe-add\" *ngIf=\"depth < maxDepth - 1\" (click)=\"addGroup(group)\">+ Groupe</button>\r\n </div>\r\n </div>\r\n</ng-template>\r\n",
25544
- styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}.fe{display:flex;flex-direction:column;color:#4d5a81;font-size:13px}.fe-tabs{display:flex;gap:4px;padding:8px 14px 0;border-bottom:1px solid #DBE4FF}.fe-tab{padding:6px 12px;border:none;border-bottom:2px solid transparent;background:none;color:#9fa9c7;font:inherit;font-weight:700;cursor:pointer}.fe-tab.active{color:#4465c9;border-bottom-color:#5272d3}.fe-body{display:flex;flex-direction:column;gap:10px;max-height:60vh;overflow-y:auto;padding:12px 14px}.fe-group{display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid #DBE4FF;border-left:4px solid #2f7d6b;border-radius:8px;background:#fff}.fe-group.or{border-left-color:#8a5a12}.fe-group.nested{background:#f6f8fd}.fe-group-head{display:flex;align-items:center;gap:8px;font-size:12px;color:#9fa9c7}.fe-group-remove{margin-left:auto}.fe-logic{display:inline-flex;border:1px solid #DBE4FF;border-radius:999px;overflow:hidden}.fe-logic button{padding:2px 10px;border:none;background:none;color:#9fa9c7;font:inherit;font-size:11px;font-weight:800;cursor:pointer}.fe-logic button.on.and{background:#2f7d6b;color:#fff}.fe-logic button.on.or{background:#8a5a12;color:#fff}.fe-cond{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) minmax(0,1.4fr) 28px;gap:6px;align-items:center}@media(max-width:600px){.fe-cond{grid-template-columns:minmax(0,1fr) minmax(0,1fr) 28px}.fe-cond .fe-value{grid-column:1/3}}select.form-control,input.form-control{height:30px;min-width:0;padding:2px 6px;font-size:13px}.fe-value{display:flex;gap:6px;min-width:0}.fe-value input{flex:1}.fe-chips{display:flex;flex-wrap:wrap;gap:4px}.fe-chip{padding:2px 8px;border:1px solid #C1D0FD;border-radius:999px;background:#fff;color:#4d5a81;font:inherit;font-size:12px;cursor:pointer}.fe-chip.on{background:#5272d3;border-color:#5272d3;color:#fff}.fe-icon{width:28px;height:28px;padding:0;border:none;border-radius:6px;background:none;color:#9fa9c7;cursor:pointer}.fe-icon:hover:not(:disabled){background:#ebf0ff;color:#4d5a81}.fe-icon:disabled{opacity:.3;cursor:default}.fe-adds{display:flex;gap:8px;flex-wrap:wrap}.fe-add{padding:3px 10px;border:1px dashed #C1D0FD;border-radius:6px;background:none;color:#4465c9;font:inherit;font-size:12px;cursor:pointer}.fe-summary{padding:8px 10px;border-radius:6px;background:#ebf0ff;font-size:12px;word-break:break-word}.fe-sort{display:grid;grid-template-columns:64px minmax(0,1fr) 120px 28px 28px 28px;gap:6px;align-items:center}.fe-sort-label{font-size:12px;color:#9fa9c7}.fe-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 14px;border-top:1px solid #DBE4FF}.fe-save{display:inline-flex;align-items:center;gap:6px;margin:0;font-weight:400;cursor:pointer}.fe-save.disabled{opacity:.55;cursor:default}.fe-save input{margin:0}.fe-clear{border:none;background:none;color:#4465c9;font:inherit;cursor:pointer}\n"],
25545
- dependencies: [
25546
- {
25547
- kind: "ngmodule",
25548
- type: CommonModule
25549
- },
25550
- {
25551
- kind: "directive",
25552
- type: i1$5.NgForOf,
25553
- selector: "[ngFor][ngForOf]",
25554
- inputs: [
25555
- "ngForOf",
25556
- "ngForTrackBy",
25557
- "ngForTemplate"
25558
- ]
25559
- },
25560
- {
25561
- kind: "directive",
25562
- type: i1$5.NgIf,
25563
- selector: "[ngIf]",
25564
- inputs: [
25565
- "ngIf",
25566
- "ngIfThen",
25567
- "ngIfElse"
25568
- ]
25569
- },
25570
- {
25571
- kind: "directive",
25572
- type: i1$5.NgTemplateOutlet,
25573
- selector: "[ngTemplateOutlet]",
25574
- inputs: [
25575
- "ngTemplateOutletContext",
25576
- "ngTemplateOutlet",
25577
- "ngTemplateOutletInjector"
25578
- ]
25579
- },
25580
- {
25581
- kind: "ngmodule",
25582
- type: FormsModule
25583
- },
25584
- {
25585
- kind: "directive",
25586
- type: i5.NgSelectOption,
25587
- selector: "option",
25588
- inputs: ["ngValue", "value"]
25589
- },
25590
- {
25591
- kind: "directive",
25592
- type: i5.ɵNgSelectMultipleOption,
25593
- selector: "option",
25594
- inputs: ["ngValue", "value"]
25595
- },
25596
- {
25597
- kind: "directive",
25598
- type: i5.DefaultValueAccessor,
25599
- selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"
25600
- },
25601
- {
25602
- kind: "directive",
25603
- type: i5.CheckboxControlValueAccessor,
25604
- selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]"
25605
- },
25606
- {
25607
- kind: "directive",
25608
- type: i5.SelectControlValueAccessor,
25609
- selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]",
25610
- inputs: ["compareWith"]
25611
- },
25612
- {
25613
- kind: "directive",
25614
- type: i5.NgControlStatus,
25615
- selector: "[formControlName],[ngModel],[formControl]"
25616
- },
25617
- {
25618
- kind: "directive",
25619
- type: i5.NgModel,
25620
- selector: "[ngModel]:not([formControlName]):not([formControl])",
25621
- inputs: [
25622
- "name",
25623
- "disabled",
25624
- "ngModel",
25625
- "ngModelOptions"
25626
- ],
25627
- outputs: ["ngModelChange"],
25628
- exportAs: ["ngModel"]
25629
- }
25630
- ],
25631
- changeDetection: i0.ChangeDetectionStrategy.Eager
25827
+ type: AppConfigService,
25828
+ providedIn: "root"
25632
25829
  });
25633
25830
  }
25634
25831
  };
@@ -25636,26 +25833,133 @@ i0.ɵɵngDeclareClassMetadata({
25636
25833
  minVersion: "12.0.0",
25637
25834
  version: "22.2.1",
25638
25835
  ngImport: i0,
25639
- type: FilterEditorComponent,
25836
+ type: AppConfigService,
25640
25837
  decorators: [{
25641
- type: Component,
25642
- args: [{
25643
- selector: "alg-filter-editor",
25644
- standalone: true,
25645
- imports: [CommonModule, FormsModule],
25646
- changeDetection: ChangeDetectionStrategy.Eager,
25647
- template: "<div class=\"fe\">\r\n <div class=\"fe-tabs\" role=\"tablist\" *ngIf=\"!section\">\r\n <button type=\"button\" class=\"fe-tab\" [class.active]=\"tab === 'filters'\" (click)=\"tab = 'filters'\">Filtres</button>\r\n <button type=\"button\" class=\"fe-tab\" [class.active]=\"tab === 'sort'\" (click)=\"tab = 'sort'\">Tri</button>\r\n </div>\r\n\r\n <div class=\"fe-body\" *ngIf=\"shownTab === 'filters'\">\r\n <ng-container *ngTemplateOutlet=\"groupTpl; context: { group: filter, parent: null, index: 0, depth: 0 }\"></ng-container>\r\n <div class=\"fe-summary\">{{ summary() }}</div>\r\n </div>\r\n\r\n <div class=\"fe-body\" *ngIf=\"shownTab === 'sort'\">\r\n <div class=\"fe-sort\" *ngFor=\"let level of sort; let i = index; trackBy: trackIndex\">\r\n <span class=\"fe-sort-label\">{{ i === 0 ? 'Trier par' : 'Puis par' }}</span>\r\n <select class=\"form-control\" [(ngModel)]=\"level.property\">\r\n <option *ngFor=\"let column of columns\" [value]=\"column.key\">{{ column.label }}</option>\r\n </select>\r\n <select class=\"form-control fe-sort-direction\" [(ngModel)]=\"level.direction\">\r\n <option value=\"asc\">Croissant</option>\r\n <option value=\"desc\">Décroissant</option>\r\n </select>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Monter\" [disabled]=\"i === 0\" (click)=\"moveSort(i, -1)\"><i class=\"fa fa-arrow-up\"></i></button>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Descendre\" [disabled]=\"i === sort.length - 1\" (click)=\"moveSort(i, 1)\"><i class=\"fa fa-arrow-down\"></i></button>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer\" (click)=\"removeSort(i)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n <div class=\"fe-adds\"><button type=\"button\" class=\"fe-add\" (click)=\"addSort()\">+ Niveau</button></div>\r\n </div>\r\n\r\n <div class=\"fe-foot\">\r\n <label class=\"fe-save\" *ngIf=\"!embedded\" [class.disabled]=\"!canSaveInView\"\r\n [title]=\"canSaveInView ? '' : 'La vue par défaut est en lecture seule : dupliquez-la pour y enregistrer un filtre'\">\r\n <input type=\"checkbox\" [(ngModel)]=\"saveInView\" [disabled]=\"!canSaveInView\" />\r\n <span>Enregistrer dans la vue{{ viewLabel ? ' « ' + viewLabel + ' »' : '' }}</span>\r\n </label>\r\n <button type=\"button\" class=\"fe-clear\" (click)=\"clear()\">Tout effacer</button>\r\n </div>\r\n</div>\r\n\r\n<!-- Groupe : relation ET / OU, conditions, groupes imbriqués. -->\r\n<ng-template #groupTpl let-group=\"group\" let-parent=\"parent\" let-index=\"index\" let-depth=\"depth\">\r\n <div class=\"fe-group\" [class.or]=\"group.Logic === 'or'\" [class.nested]=\"depth > 0\">\r\n <div class=\"fe-group-head\">\r\n <span class=\"fe-logic\">\r\n <button type=\"button\" [class.on]=\"group.Logic === 'and'\" class=\"and\" (click)=\"setLogic(group, 'and')\">ET</button>\r\n <button type=\"button\" [class.on]=\"group.Logic === 'or'\" class=\"or\" (click)=\"setLogic(group, 'or')\">OU</button>\r\n </span>\r\n <span class=\"fe-group-hint\">{{ group.Logic === 'and' ? 'Toutes les conditions' : 'Au moins une condition' }}</span>\r\n <button type=\"button\" *ngIf=\"parent\" class=\"fe-icon fe-group-remove\" title=\"Retirer le groupe\" (click)=\"removeGroup(parent, index)\">\r\n <i class=\"fa fa-times\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"fe-cond\" *ngFor=\"let condition of group.Conditions; let i = index; trackBy: trackIndex\">\r\n <select class=\"form-control\" [(ngModel)]=\"condition.Property\" (ngModelChange)=\"onColumnChange(condition)\">\r\n <option *ngFor=\"let column of columns\" [value]=\"column.key\">{{ column.label }}</option>\r\n </select>\r\n <select class=\"form-control\" [(ngModel)]=\"condition.Operator\" (ngModelChange)=\"onOperatorChange(condition)\">\r\n <option *ngFor=\"let op of operatorsOf(condition)\" [value]=\"op.value\">{{ op.label }}</option>\r\n </select>\r\n <span class=\"fe-value\">\r\n <ng-container *ngIf=\"isList(condition); else valueTpl\">\r\n <span class=\"fe-chips\">\r\n <button type=\"button\" *ngFor=\"let option of columnOf(condition.Property)?.values\" class=\"fe-chip\"\r\n [class.on]=\"condition.Values?.includes(option.value)\" (click)=\"toggleValue(condition, option.value)\">{{ option.label }}</button>\r\n </span>\r\n </ng-container>\r\n <ng-template #valueTpl>\r\n <ng-container *ngIf=\"hasValue(condition)\">\r\n <input class=\"form-control\" [type]=\"inputType(condition)\" [(ngModel)]=\"condition.Value\" />\r\n <input *ngIf=\"hasSecondValue(condition)\" class=\"form-control\" [type]=\"inputType(condition)\" [(ngModel)]=\"condition.Value2\" />\r\n </ng-container>\r\n </ng-template>\r\n </span>\r\n <button type=\"button\" class=\"fe-icon\" title=\"Retirer la condition\" (click)=\"removeCondition(group, i)\"><i class=\"fa fa-times\"></i></button>\r\n </div>\r\n\r\n <ng-container *ngFor=\"let sub of group.Groups; let gi = index; trackBy: trackIndex\">\r\n <ng-container *ngTemplateOutlet=\"groupTpl; context: { group: sub, parent: group, index: gi, depth: depth + 1 }\"></ng-container>\r\n </ng-container>\r\n\r\n <div class=\"fe-adds\">\r\n <button type=\"button\" class=\"fe-add\" (click)=\"addCondition(group)\">+ Condition</button>\r\n <button type=\"button\" class=\"fe-add\" *ngIf=\"depth < maxDepth - 1\" (click)=\"addGroup(group)\">+ Groupe</button>\r\n </div>\r\n </div>\r\n</ng-template>\r\n",
25648
- styles: [".home-page{font-family:Open sans,sans-serif;background:#fff;-webkit-font-smoothing:antialiased;height:100%;width:100%;padding:16px}.main-container{margin:0 50px}.main-container-modal{width:calc(100% - 50px);margin:10px 25px 0}.moyen-titre{font-weight:700;font-size:24px;color:#4d5a81;margin:8px 0;border-bottom:solid 1px #DBE4FF}.grand-titre{font-weight:700;font-size:28px;color:#4d5a81;margin:0 0 8px}.petit-titre{font-weight:700;font-size:16px;color:#4d5a81;margin:0 0 8px}.petit-titre.underlined{border-bottom:solid 1px #4D5A81}.paragraph{padding:0 15px}.paragraph-highlight{padding:5px 15px 0 12px;border-left:5px solid #8EA7F8;border-bottom:1px solid #DBE4FF;border-top:1px solid #DBE4FF;border-right:1px solid #DBE4FF;background-color:#f7faff}.highlight{background-color:#f7faff;border:1px solid #DBE4FF;border-radius:5px;padding:15px;color:#3c5dc4;margin:0 0 16px}.highlight p{margin-bottom:0}.highlight:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"53.294\" height=\"50.629\" viewBox=\"0 0 53.294 50.629\">%0D%0A <path id=\"allerg\\e8ne\" d=\"M830.494,25.793l-5.814,6.783.727,8.963-8.721,1.938-4.6,7.752L803.847,47.6l-8.236,3.634-4.6-7.51-8.721-1.938.727-8.963L777.2,26.035l5.814-6.783-.727-8.963,8.721-1.938L795.61.6l8.236,3.634L812.083.6l4.6,7.752,8.721,1.938-.727,8.963Zm-24.467,2.422V13.681h-4.845V28.216Zm0,9.69V33.06h-4.845v4.845Z\" transform=\"translate(-777.2 -0.6)\"/>%0D%0A</svg>%0D%0A');background-repeat:no-repeat;margin:0 8px 24px 0;filter:invert(31%) sepia(51%) saturate(1323%) hue-rotate(199deg) brightness(99%) contrast(94%)}.notification{background-color:#dbe4ff;border:1px solid #C4D0F9;border-radius:5px;color:#4d5a81;margin:16px;padding:5px 15px;text-align:justify}.notification:before{position:relative;display:block;float:left;width:24px;height:24px;content:\" \";background-size:24px 24px;-webkit-background-size:24px 24px;background-image:url('data:image/svg+xml,<svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"fas\" data-icon=\"info-circle\" class=\"svg-inline--fa fa-info-circle fa-w-16\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"></path></svg>');background-repeat:no-repeat;margin:0 8px 0 0;filter:invert(31%) sepia(8%) saturate(2329%) hue-rotate(188deg) brightness(103%) contrast(85%)}.notification-text{padding:0 15px 0 30px;font-size:16px}.fe{display:flex;flex-direction:column;color:#4d5a81;font-size:13px}.fe-tabs{display:flex;gap:4px;padding:8px 14px 0;border-bottom:1px solid #DBE4FF}.fe-tab{padding:6px 12px;border:none;border-bottom:2px solid transparent;background:none;color:#9fa9c7;font:inherit;font-weight:700;cursor:pointer}.fe-tab.active{color:#4465c9;border-bottom-color:#5272d3}.fe-body{display:flex;flex-direction:column;gap:10px;max-height:60vh;overflow-y:auto;padding:12px 14px}.fe-group{display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid #DBE4FF;border-left:4px solid #2f7d6b;border-radius:8px;background:#fff}.fe-group.or{border-left-color:#8a5a12}.fe-group.nested{background:#f6f8fd}.fe-group-head{display:flex;align-items:center;gap:8px;font-size:12px;color:#9fa9c7}.fe-group-remove{margin-left:auto}.fe-logic{display:inline-flex;border:1px solid #DBE4FF;border-radius:999px;overflow:hidden}.fe-logic button{padding:2px 10px;border:none;background:none;color:#9fa9c7;font:inherit;font-size:11px;font-weight:800;cursor:pointer}.fe-logic button.on.and{background:#2f7d6b;color:#fff}.fe-logic button.on.or{background:#8a5a12;color:#fff}.fe-cond{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) minmax(0,1.4fr) 28px;gap:6px;align-items:center}@media(max-width:600px){.fe-cond{grid-template-columns:minmax(0,1fr) minmax(0,1fr) 28px}.fe-cond .fe-value{grid-column:1/3}}select.form-control,input.form-control{height:30px;min-width:0;padding:2px 6px;font-size:13px}.fe-value{display:flex;gap:6px;min-width:0}.fe-value input{flex:1}.fe-chips{display:flex;flex-wrap:wrap;gap:4px}.fe-chip{padding:2px 8px;border:1px solid #C1D0FD;border-radius:999px;background:#fff;color:#4d5a81;font:inherit;font-size:12px;cursor:pointer}.fe-chip.on{background:#5272d3;border-color:#5272d3;color:#fff}.fe-icon{width:28px;height:28px;padding:0;border:none;border-radius:6px;background:none;color:#9fa9c7;cursor:pointer}.fe-icon:hover:not(:disabled){background:#ebf0ff;color:#4d5a81}.fe-icon:disabled{opacity:.3;cursor:default}.fe-adds{display:flex;gap:8px;flex-wrap:wrap}.fe-add{padding:3px 10px;border:1px dashed #C1D0FD;border-radius:6px;background:none;color:#4465c9;font:inherit;font-size:12px;cursor:pointer}.fe-summary{padding:8px 10px;border-radius:6px;background:#ebf0ff;font-size:12px;word-break:break-word}.fe-sort{display:grid;grid-template-columns:64px minmax(0,1fr) 120px 28px 28px 28px;gap:6px;align-items:center}.fe-sort-label{font-size:12px;color:#9fa9c7}.fe-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 14px;border-top:1px solid #DBE4FF}.fe-save{display:inline-flex;align-items:center;gap:6px;margin:0;font-weight:400;cursor:pointer}.fe-save.disabled{opacity:.55;cursor:default}.fe-save input{margin:0}.fe-clear{border:none;background:none;color:#4465c9;font:inherit;cursor:pointer}\n"]
25649
- }]
25838
+ type: Injectable,
25839
+ args: [{ providedIn: "root" }]
25650
25840
  }],
25651
- propDecorators: {
25652
- columns: [{ type: Input }],
25653
- filter: [{ type: Input }],
25654
- sort: [{ type: Input }],
25655
- embedded: [{ type: Input }],
25656
- section: [{ type: Input }]
25657
- }
25841
+ ctorParameters: () => [{ type: ApplicationService }]
25658
25842
  });
25659
- export { Action, ActionContext, ActionDefinition, ActionHandlerContext, ActionPanelComponent, ActionService, ActionsComponent, ActionsContentComponent, AddclassDirective, AlertDialog, AlertMessage, AlertService, AppConfigService, ApplicationService, BaseArrayParameterTypeComponent, BaseComponent, BaseEditorComponent, BaseEntityEditor, BaseFormContext, BaseItemListComponent, BaseParameterTypeComponent, BaseShortcutsDirective, BreadcrumbComponent, CalendarComponent, CanDeactivateGuard, CardListComponent, CellRendererComponent, ChangeDetectionContext, ChangeUtilities, CollapseDirective, ConfirmModalComponent, ConfirmOptions, ContextChoice, CoreUxModule, DataContext, DataGridComponent, DataGridStyleContext, DataListComponent, DatagridCellComponent, DatagridCustomComponent, DatePickerDirective, DecimalInputDirective, DefaultDataGridStyleContext, DefaultFilterComponent, DefaultLayoutComponent, DflatBaseComponent, DflatBaseFilterComponent, DflatModalFrameComponent, DflatModalOptions, DflatModalRef, DflatModalService, DialogComponent, DialogComponentParameter, DialogType, DraggableDirective, DroppableDirective, DynamicFieldComponent, DynamicFilterComponent, DynamicFilterExtraComponent, DynamicFilterPanel, DynamicFormComponent, EXTENDED_ACTION_COMPONENT, EditModeDirective, EditorContext, EditorTabComponent, EditorTabSetComponent, EntityConfigContext, EntityFacade, EntityFacadeRegistryService, EntitySections, EntitySelectors, EntityUpdater, ExportDataModalComponent, ExtendedAction, ExtendedActionComponent, FacadeLoadGuard, FacadeModalService, FieldComponent, FilterContext, FilterEditorComponent, FilterFormModalComponent, FilterLifecycleGuard, FilterOperators, FilterSummaryComponent, FilterSummaryPanelComponent, FireAuthService, FocusableDirective, FormPasswordComponent, FormWrapper, GridFieldComponent, GridRowComponent, GridStylePipe, GridViewMenuComponent, HideOnThresholdDirective, IIsCancelWithWarning, ImageComponent, ImageService, InputSelectorComponent, InputSelectorOptions, InputTreeSelectorComponent, InsertionDirective, LayoutContext, LayoutHandlerDirective, LayoutService, LoginLayoutComponent, LogsComponent, MenuComponent, MenuDashboardComponent, MessageSeverity, MobileMenuComponent, ModalService, ModalStateService, NewEditorComponent, NewPaginatorComponent, OldDataPageComponent, OldLayoutHandlerDirective, OnEnterDirective, OpenNavigationMenuEvent, OrderableModalComponent, OrderableModalService, PP_SERVICE, PageLayoutComponent, PageTabRouteReuseStrategy, PageTabService, PageTabsComponent, PaginatorComponent, ParameterFieldComponent, ParameterTypeBoolArrayComponent, ParameterTypeBoolComponent, ParameterTypeDateArrayComponent, ParameterTypeDateComponent, ParameterTypeNumberArrayComponent, ParameterTypeNumberComponent, ParameterTypePasswordComponent, ParameterTypeTextArrayComponent, ParameterTypeTextComponent, PropertyChangeBus, PropertyEditorComponent, PropertyGridComponent, ROOT_DEPS, RowRefDirective, RowRefHost, RowRegistry, SearchResultsComponent, SectionContext, SelectionContext, SelectionService, SelectionStyler, SelectionType, SelectorComponent, SelectorDataSource, SelectorMapperServiceBase, SelectorRegistry, SortableColumnComponent, SortingState, StandardPageLayoutComponent, TRACK_RECORD_MODAL_COMPONENT, Tab, TabHeadingDirective, TabTranscludeDirective, Tabset, TestService, TextViewerComponent, TimePickerComponent, ToDefinePipe, ToFixedSizeIntegerPipe, TooltipDirective, TrackRecordModalComponent, TreeComponent, ValidateOnEnterDirective, ValidationErrorComponent, ValidationModalComponent, ValidationService, ViewFilterNoValueOperators, ViewModeDirective, ViewModelLoaderService, WizardComponent, WizardStepComponent, additionalQueryOf, downloadFile, filterFromId, formatDecimal, layoutKeyToString, normalisedOrder, normalizeViewFilter, parseDecimal, parseViewQuery, toAdditionalQuery, toViewQuery };
25843
+ var BaseFormContext = class {
25844
+ constructor(fb, validationService) {
25845
+ this.fb = fb;
25846
+ this.validationService = validationService;
25847
+ this.fields = [];
25848
+ this.trackByField = (_, d) => d.PropertyName;
25849
+ }
25850
+ initPropertyForm(withValidators = false) {
25851
+ if (!this.fields) return;
25852
+ if (!this.form) this.form = new FormGroup({});
25853
+ else this.form.reset();
25854
+ this.fields.forEach((field) => {
25855
+ const validators = withValidators ? this.validationService.getFieldValidators(field) : [];
25856
+ const controlValue = this.entity?.hasOwnProperty(field.PropertyName) && this.entity[field.PropertyName] !== void 0 ? this.entity[field.PropertyName] : null;
25857
+ if (!this.form.contains(field.PropertyName)) this.form.addControl(field.PropertyName, new FormControl(controlValue, validators));
25858
+ });
25859
+ if (this.entity) this.form.patchValue(this.entity);
25860
+ }
25861
+ triggerField(desc) {
25862
+ if (desc.IsConditioning) this.handleConditional(desc);
25863
+ if (desc.CascadeDependency) this.handleCascade(desc);
25864
+ }
25865
+ setupCascade(descriptors) {
25866
+ descriptors.forEach((desc) => {
25867
+ if (!desc.CascadeDependency || !desc.CascadeDependencyProperty) return;
25868
+ this.form.get(desc.PropertyName)?.valueChanges.subscribe(() => this.handleCascade(desc));
25869
+ });
25870
+ }
25871
+ setupConditional(descriptors) {
25872
+ descriptors.forEach((desc) => {
25873
+ if (!desc.IsConditioning) return;
25874
+ this.form.get(desc.PropertyName)?.valueChanges.subscribe(() => this.handleConditional(desc));
25875
+ });
25876
+ }
25877
+ changes$() {
25878
+ return this.form.valueChanges.pipe(debounceTime$1(50), distinctUntilChanged$1(), shareReplay$1(1));
25879
+ }
25880
+ patch(values) {
25881
+ this.form.patchValue(values, { emitEvent: true });
25882
+ }
25883
+ serialize() {
25884
+ return this.form.getRawValue();
25885
+ }
25886
+ handleCascade(field) {
25887
+ const selectedValue = field.FilterType === FilterType.Integer ? Number(this.entity[field.PropertyName]) : this.entity[field.PropertyName];
25888
+ const props = field.CascadeDependencyProperty.split(";");
25889
+ const dependentFields = this.fields.filter((f) => props.includes(f.PropertyName));
25890
+ for (const dependingField of dependentFields) {
25891
+ dependingField.CurrentFilters = dependingField.CurrentFilters || {};
25892
+ if (Utilities.IsNil(selectedValue) || Utilities.isEmptyString(selectedValue)) delete dependingField.CurrentFilters[field.PropertyName];
25893
+ else dependingField.CurrentFilters[field.PropertyName] = true;
25894
+ this.resetFieldData(dependingField);
25895
+ if (field.FilterDistinct) dependingField.Data = [...new Map(dependingField.Data.map((item) => [item[dependingField.PropertyName], item])).values()];
25896
+ }
25897
+ }
25898
+ handleConditional(field) {
25899
+ const allFields = this.fields.filter((f) => f.ConditioningField === field.PropertyName);
25900
+ const conditioningValue = this.form.get(field.PropertyName)?.value;
25901
+ for (let conditionedField of allFields) {
25902
+ const conditionMet = Utilities.IsNil(conditioningValue) || conditionedField.ConditioningFieldValue.includes(conditioningValue) || conditionedField.BoolConditioningFieldValue === conditioningValue;
25903
+ if (conditionedField.ConditionalResult === ConditionalResults.Hide) conditionedField.IsHidden = conditionMet;
25904
+ if (conditionedField.ConditionalResult === ConditionalResults.ReadOnly) conditionedField.ReadOnly = conditionMet;
25905
+ const control = this.form.get(conditionedField.PropertyName);
25906
+ if (control) {
25907
+ if (conditionedField.IsHidden) control.clearValidators();
25908
+ else {
25909
+ const validators = this.validationService.getValidators(conditionedField.FieldValidators);
25910
+ this.updateControlValidators(control, validators);
25911
+ }
25912
+ }
25913
+ }
25914
+ }
25915
+ resetFieldData(field) {
25916
+ if (Utilities.IsNil(field.OriginalData)) return;
25917
+ if (field.FilterType === FilterType.Integer) field.Data = field.OriginalData.filter((p) => p[field.ValueMember] === -1 || Object.keys(field.CurrentFilters).every((name) => p[name] === this.entity[name]));
25918
+ else field.Data = field.OriginalData.filter((p) => Object.keys(field.CurrentFilters).every((name) => p[name] === this.entity[name]));
25919
+ }
25920
+ updateControlValidators(ctrl, validators) {
25921
+ ctrl.setValidators(validators);
25922
+ ctrl.updateValueAndValidity({ emitEvent: false });
25923
+ }
25924
+ };
25925
+ var SortingState = class {
25926
+ constructor(entityKey) {
25927
+ this.entityKey = entityKey;
25928
+ this.property$ = new BehaviorSubject(" ");
25929
+ this.direction$ = new BehaviorSubject("desc");
25930
+ }
25931
+ get changes$() {
25932
+ return this.property$.pipe(distinctUntilChanged(), (source) => new Observable((observer) => {
25933
+ const sub1 = this.property$.subscribe(() => observer.next(this.snapshot()));
25934
+ const sub2 = this.direction$.subscribe(() => observer.next(this.snapshot()));
25935
+ return () => {
25936
+ sub1.unsubscribe();
25937
+ sub2.unsubscribe();
25938
+ };
25939
+ }));
25940
+ }
25941
+ snapshot() {
25942
+ return {
25943
+ property: this.property$.value,
25944
+ direction: this.direction$.value
25945
+ };
25946
+ }
25947
+ updateProperty(property) {
25948
+ if (this.property$.value === property) this.toggleDirection();
25949
+ else {
25950
+ this.property$.next(property);
25951
+ this.direction$.next("asc");
25952
+ }
25953
+ }
25954
+ toggleDirection() {
25955
+ const next = this.direction$.value === "asc" ? "desc" : "asc";
25956
+ this.direction$.next(next);
25957
+ }
25958
+ reset() {
25959
+ this.property$.next("");
25960
+ this.direction$.next("asc");
25961
+ }
25962
+ };
25963
+ export { Action, ActionContext, ActionDefinition, ActionHandlerContext, ActionPanelComponent, ActionService, ActionsComponent, ActionsContentComponent, AddclassDirective, AlertDialog, AlertMessage, AlertService, AppConfigService, ApplicationService, BaseArrayParameterTypeComponent, BaseComponent, BaseEditorComponent, BaseEntityEditor, BaseFormContext, BaseItemListComponent, BaseParameterTypeComponent, BaseShortcutsDirective, BreadcrumbComponent, CalendarComponent, CanDeactivateGuard, CardListComponent, CellRendererComponent, ChangeDetectionContext, ChangeUtilities, CollapseDirective, ConfirmModalComponent, ConfirmOptions, ContextChoice, CoreUxModule, DataContext, DataGridComponent, DataGridStyleContext, DataListComponent, DatagridCellComponent, DatagridCustomComponent, DatePickerDirective, DecimalInputDirective, DefaultDataGridStyleContext, DefaultFilterComponent, DefaultLayoutComponent, DflatBaseComponent, DflatBaseFilterComponent, DflatModalFrameComponent, DflatModalOptions, DflatModalRef, DflatModalService, DialogComponent, DialogComponentParameter, DialogType, DraggableDirective, DroppableDirective, DynamicFieldComponent, DynamicFilterComponent, DynamicFilterExtraComponent, DynamicFilterPanel, DynamicFormComponent, EXTENDED_ACTION_COMPONENT, EditModeDirective, EditorContext, EditorTabComponent, EditorTabSetComponent, EntityConfigContext, EntityFacade, EntityFacadeRegistryService, EntitySections, EntitySelectors, EntityUpdater, ExportDataModalComponent, ExtendedAction, ExtendedActionComponent, FacadeLoadGuard, FacadeModalService, FieldComponent, FilterContext, FilterEditorComponent, FilterFormModalComponent, FilterLifecycleGuard, FilterOperators, FilterSummaryComponent, FilterSummaryPanelComponent, FireAuthService, FocusableDirective, FormPasswordComponent, FormWrapper, GridFieldComponent, GridRowComponent, GridStylePipe, GridViewMenuComponent, HideOnThresholdDirective, IIsCancelWithWarning, ImageComponent, ImageService, InputSelectorComponent, InputSelectorOptions, InputTreeSelectorComponent, InsertionDirective, LayoutContext, LayoutHandlerDirective, LayoutService, LoginLayoutComponent, LogsComponent, MenuComponent, MenuDashboardComponent, MessageSeverity, MobileMenuComponent, ModalService, ModalStateService, NewEditorComponent, NewPaginatorComponent, OldDataPageComponent, OldLayoutHandlerDirective, OnEnterDirective, OpenNavigationMenuEvent, OrderableModalComponent, OrderableModalService, PP_SERVICE, PageLayoutComponent, PageTabRouteReuseStrategy, PageTabService, PageTabsComponent, PaginatorComponent, ParameterFieldComponent, ParameterTypeBoolArrayComponent, ParameterTypeBoolComponent, ParameterTypeDateArrayComponent, ParameterTypeDateComponent, ParameterTypeNumberArrayComponent, ParameterTypeNumberComponent, ParameterTypePasswordComponent, ParameterTypeTextArrayComponent, ParameterTypeTextComponent, PropertyChangeBus, PropertyEditorComponent, PropertyGridComponent, ROOT_DEPS, RowRefDirective, RowRefHost, RowRegistry, SearchResultsComponent, SectionContext, SelectionContext, SelectionService, SelectionStyler, SelectionType, SelectorComponent, SelectorDataSource, SelectorMapperServiceBase, SelectorRegistry, SortableColumnComponent, SortingState, StandardPageLayoutComponent, TRACK_RECORD_MODAL_COMPONENT, Tab, TabHeadingDirective, TabTranscludeDirective, Tabset, TestService, TextViewerComponent, TimePickerComponent, ToDefinePipe, ToFixedSizeIntegerPipe, TooltipDirective, TrackRecordModalComponent, TreeComponent, ValidateOnEnterDirective, ValidationErrorComponent, ValidationModalComponent, ValidationService, ViewFilterNoValueOperators, ViewModeDirective, ViewModelLoaderService, WizardComponent, WizardStepComponent, additionalQueryOf, downloadFile, filterFromId, formatDecimal, layoutKeyToString, normalisedOrder, normalizeViewFilter, parseDecimal, parseViewQuery, toAdditionalQuery, toViewQuery, viewPanelOf, withoutViewPanel };
25660
25964
 
25661
25965
  //# sourceMappingURL=dflat-core-web-core-ux.mjs.map