@cadriciel/ui 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ import { PortalModule, ComponentPortal, TemplatePortal, Portal } from '@angular/
11
11
  import * as i2 from '@angular/cdk/a11y';
12
12
  import { A11yModule, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
13
13
  import { Subject, isObservable } from 'rxjs';
14
- import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle, CdkDragPlaceholder, transferArrayItem } from '@angular/cdk/drag-drop';
14
+ import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle, CdkDragPlaceholder, transferArrayItem, CdkDropListGroup } from '@angular/cdk/drag-drop';
15
15
  import { RouterLink, Router, RouterLinkActive } from '@angular/router';
16
16
  import { HttpClient, HttpHeaders, HttpEventType } from '@angular/common/http';
17
17
 
@@ -4081,14 +4081,14 @@ class CadDialogContainerComponent {
4081
4081
  const c = this.config();
4082
4082
  return ['cad-dialog-container', c.kind === 'drawer' ? `is-drawer pos-${c.drawerPosition ?? 'right'}` : '', c.styleClass ?? ''].join(' ');
4083
4083
  }, ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
4084
- static SIZES = { sm: '360px', md: '480px', lg: '640px', xl: '900px', full: '100vw' };
4084
+ static SIZES = { sm: '360px', md: '480px', lg: '640px', xl: '900px', full: '100%' };
4085
4085
  static DRAWER_SIZES = { sm: '320px', md: '420px', lg: '600px', full: '100%' };
4086
4086
  width = computed(() => {
4087
4087
  const c = this.config();
4088
4088
  if (c.kind === 'drawer') {
4089
4089
  const s = this.bp.isMobile() ? (c.drawerMobileSize ?? 'full') : (c.drawerSize ?? 'md');
4090
4090
  const vertical = c.drawerPosition === 'top' || c.drawerPosition === 'bottom';
4091
- return vertical ? '100vw' : (CadDialogContainerComponent.DRAWER_SIZES[s] ?? s);
4091
+ return vertical ? '100%' : (CadDialogContainerComponent.DRAWER_SIZES[s] ?? s);
4092
4092
  }
4093
4093
  return c.width ?? CadDialogContainerComponent.SIZES[c.size ?? 'md'] ?? '480px';
4094
4094
  }, ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
@@ -4103,7 +4103,7 @@ class CadDialogContainerComponent {
4103
4103
  }
4104
4104
  return c.height ?? null;
4105
4105
  }, ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
4106
- maxWidth = computed(() => this.config().kind === 'drawer' ? '100vw' : (this.config().maxWidth ?? (this.config().size === 'full' ? '100vw' : '92vw')), ...(ngDevMode ? [{ debugName: "maxWidth" }] : /* istanbul ignore next */ []));
4106
+ maxWidth = computed(() => this.config().kind === 'drawer' ? '100%' : (this.config().maxWidth ?? (this.config().size === 'full' ? '100%' : '92%')), ...(ngDevMode ? [{ debugName: "maxWidth" }] : /* istanbul ignore next */ []));
4107
4107
  drag = signal(null, ...(ngDevMode ? [{ debugName: "drag" }] : /* istanbul ignore next */ []));
4108
4108
  dragTransform = computed(() => { const d = this.drag(); return d ? `translate(${d.x}px, ${d.y}px)` : null; }, ...(ngDevMode ? [{ debugName: "dragTransform" }] : /* istanbul ignore next */ []));
4109
4109
  toggleMaximize() { this.maximized.set(!this.maximized()); this.drag.set(null); this.maximizeChange.emit(this.maximized()); }
@@ -4148,7 +4148,7 @@ class CadDialogContainerComponent {
4148
4148
  <footer class="cad-dialog__footer"><ng-container *ngTemplateOutlet="config().footer!" /></footer>
4149
4149
  }
4150
4150
  </div>
4151
- `, isInline: true, styles: [".cdk-overlay-container{position:fixed;z-index:var(--cad-z-overlay, 100002);pointer-events:none;top:0;left:0;width:100%;height:100%}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000;width:100%;height:100%}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000;display:flex;max-width:100%;max-height:100%}.cdk-overlay-backdrop{position:absolute;inset:0;z-index:1000;pointer-events:auto;-webkit-tap-highlight-color:transparent;opacity:0;transition:opacity var(--cad-dur, .14s) var(--cad-ease, ease)}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:1}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing{visibility:visible;opacity:0}.cdk-overlay-connected-position-bounding-box{position:absolute;z-index:1000;display:flex;flex-direction:column;min-width:1px;min-height:1px}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.cad-overlay-backdrop{background:var(--cad-backdrop, rgba(2, 6, 23, .45))}.cad-overlay-backdrop.is-hidden{display:none}.cad-overlay-pane{pointer-events:auto}.cad-overlay-pane.is-drawer-pane{height:100%}.cad-dialog-container{display:flex;outline:none;font-family:var(--cad-font);font-size:var(--cad-font-size);color:var(--cad-fg);max-height:100%;max-width:100%}.cad-dialog-container.is-hidden{display:none}.cad-dialog{display:flex;flex-direction:column;background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:var(--cad-radius-lg);box-shadow:var(--cad-shadow-lg);max-height:92vh;min-width:0;outline:none;box-sizing:border-box;opacity:0;transform:translateY(6px) scale(.98);transition:opacity var(--cad-dur) var(--cad-ease),transform var(--cad-dur) var(--cad-ease)}.cad-dialog-container.is-open .cad-dialog{opacity:1;transform:none}.cad-dialog-container.is-maximized .cad-dialog{width:100vw!important;max-width:100vw!important;height:100vh!important;max-height:100vh;border-radius:0}.cad-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:14px 16px 10px;flex:none}.cad-dialog__header.is-draggable{cursor:move;-webkit-user-select:none;user-select:none}.cad-dialog__title{margin:0;font-size:14.5px;font-weight:600;color:var(--cad-fg);min-width:0}.cad-dialog__actions{display:inline-flex;gap:2px;margin-left:auto;flex:none}.cad-dialog__icon-btn{display:grid;place-items:center;width:26px;height:26px;border:0;border-radius:var(--cad-radius-sm);background:transparent;color:var(--cad-fg-muted);cursor:pointer;padding:0;transition:background var(--cad-dur),color var(--cad-dur)}.cad-dialog__icon-btn:hover{background:var(--cad-hover);color:var(--cad-fg)}.cad-dialog__icon-btn:focus-visible{outline:2px solid var(--cad-ring);outline-offset:1px}.cad-dialog__body{padding:4px 16px 14px;color:var(--cad-fg-secondary);font-size:var(--cad-font-size);overflow:auto;flex:1;min-height:0}.cad-dialog__body:first-child{padding-top:16px}.cad-dialog__footer{display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:12px 16px;border-top:1px solid var(--cad-border);background:var(--cad-bg-subtle);border-radius:0 0 var(--cad-radius-lg) var(--cad-radius-lg);flex:none}.cad-dialog-container.is-drawer{height:100%}.cad-dialog-container.is-drawer .cad-dialog{max-height:none;height:100%;border-radius:0;opacity:1;transform:none;transition:transform .18s var(--cad-ease)}.cad-dialog-container.is-drawer .cad-dialog__footer{border-radius:0}.cad-dialog-container.is-drawer.pos-right .cad-dialog{transform:translate(100%);border-width:0 0 0 1px}.cad-dialog-container.is-drawer.pos-left .cad-dialog{transform:translate(-100%);border-width:0 1px 0 0}.cad-dialog-container.is-drawer.pos-top .cad-dialog{transform:translateY(-100%);border-width:0 0 1px 0}.cad-dialog-container.is-drawer.pos-bottom .cad-dialog{transform:translateY(100%);border-width:1px 0 0 0}.cad-dialog-container.is-drawer.is-open .cad-dialog{transform:none}.cad-dialog-container.is-drawer.pos-top,.cad-dialog-container.is-drawer.pos-bottom{width:100%}.cad-dialog-container.is-drawer.pos-top .cad-dialog,.cad-dialog-container.is-drawer.pos-bottom .cad-dialog{width:100vw!important;max-width:100vw!important}.cad-dialog-container.is-mobile-full{width:100vw;height:100dvh;max-height:100dvh}.cad-dialog-container.is-mobile-full .cad-dialog{width:100vw!important;max-width:100vw!important;height:100dvh!important;max-height:100dvh;border-radius:0;border:0;box-shadow:none;padding-bottom:env(safe-area-inset-bottom)}.cad-dialog-container.is-mobile-full .cad-dialog__header{position:sticky;top:0;background:var(--cad-surface);z-index:1;padding-top:calc(14px + env(safe-area-inset-top))}.cad-dialog-container.is-mobile-full .cad-dialog__footer{border-radius:0}.cad-overlay-pane.is-mobile-sheet-pane{width:100%;max-width:100%}.cad-dialog-container.is-mobile-sheet{width:100vw;align-items:flex-end}.cad-dialog-container.is-mobile-sheet .cad-dialog{position:relative;width:100vw!important;max-width:100vw!important;height:92dvh!important;max-height:92dvh;border-radius:16px 16px 0 0;border-bottom:0;box-shadow:0 -8px 32px #0000002e;padding-bottom:env(safe-area-inset-bottom);opacity:1;transform:translateY(100%);transition:transform .2s var(--cad-ease)}.cad-dialog-container.is-mobile-sheet.sheet-h-auto .cad-dialog{height:auto!important}.cad-dialog-container.is-mobile-sheet .cad-dialog:before{content:\"\";position:absolute;top:8px;left:50%;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--cad-border-strong)}.cad-dialog-container.is-mobile-sheet .cad-dialog__header{padding-top:20px}.cad-dialog-container.is-mobile-sheet .cad-dialog__body:first-child{padding-top:22px}.cad-dialog-container.is-mobile-sheet.is-open .cad-dialog{transform:none}.cad-dialog-container.is-mobile-sheet .cad-dialog__footer{border-radius:0}.cad-confirm-dialog.is-mobile-sheet .cad-dialog__footer{flex-direction:column-reverse;align-items:stretch;gap:8px}.cad-confirm-dialog.is-mobile-sheet .cad-dialog__footer>*{width:100%;height:44px}@media(prefers-reduced-motion:reduce){.cad-dialog,.cad-dialog-container.is-drawer .cad-dialog,.cad-dialog-container.is-mobile-sheet .cad-dialog,.cdk-overlay-backdrop{transition:none!important}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i1$1.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
4151
+ `, isInline: true, styles: [".cdk-overlay-container{position:fixed;z-index:var(--cad-z-overlay, 100002);pointer-events:none;top:0;left:0;width:100%;height:100%}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000;width:100%;height:100%}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000;display:flex;max-width:100%;max-height:100%}.cdk-overlay-backdrop{position:absolute;inset:0;z-index:1000;pointer-events:auto;-webkit-tap-highlight-color:transparent;opacity:0;transition:opacity var(--cad-dur, .14s) var(--cad-ease, ease)}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:1}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing{visibility:visible;opacity:0}.cdk-overlay-connected-position-bounding-box{position:absolute;z-index:1000;display:flex;flex-direction:column;min-width:1px;min-height:1px}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.cad-overlay-backdrop{background:var(--cad-backdrop, rgba(2, 6, 23, .45))}.cad-overlay-backdrop.is-hidden{display:none}.cad-overlay-pane{pointer-events:auto}.cad-overlay-pane.is-drawer-pane{height:100%}.cad-dialog-container{display:flex;outline:none;font-family:var(--cad-font);font-size:var(--cad-font-size);color:var(--cad-fg);max-height:100%;max-width:100%}.cad-dialog-container.is-hidden{display:none}.cad-dialog{display:flex;flex-direction:column;background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:var(--cad-radius-lg);box-shadow:var(--cad-shadow-lg);max-height:92vh;min-width:0;outline:none;box-sizing:border-box;opacity:0;transform:translateY(6px) scale(.98);transition:opacity var(--cad-dur) var(--cad-ease),transform var(--cad-dur) var(--cad-ease)}.cad-dialog-container.is-open .cad-dialog{opacity:1;transform:none}.cad-dialog-container.is-maximized .cad-dialog{width:100%!important;max-width:100%!important;height:100vh!important;max-height:100vh;border-radius:0}.cad-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:14px 16px 10px;flex:none}.cad-dialog__header.is-draggable{cursor:move;-webkit-user-select:none;user-select:none}.cad-dialog__title{margin:0;font-size:14.5px;font-weight:600;color:var(--cad-fg);min-width:0}.cad-dialog__actions{display:inline-flex;gap:2px;margin-left:auto;flex:none}.cad-dialog__icon-btn{display:grid;place-items:center;width:26px;height:26px;border:0;border-radius:var(--cad-radius-sm);background:transparent;color:var(--cad-fg-muted);cursor:pointer;padding:0;transition:background var(--cad-dur),color var(--cad-dur)}.cad-dialog__icon-btn:hover{background:var(--cad-hover);color:var(--cad-fg)}.cad-dialog__icon-btn:focus-visible{outline:2px solid var(--cad-ring);outline-offset:1px}.cad-dialog__body{padding:4px 16px 14px;color:var(--cad-fg-secondary);font-size:var(--cad-font-size);overflow:auto;flex:1;min-height:0}.cad-dialog__body:first-child{padding-top:16px}.cad-dialog__footer{display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:12px 16px;border-top:1px solid var(--cad-border);background:var(--cad-bg-subtle);border-radius:0 0 var(--cad-radius-lg) var(--cad-radius-lg);flex:none}.cad-dialog-container.is-drawer{height:100%}.cad-dialog-container.is-drawer .cad-dialog{max-height:none;height:100%;border-radius:0;opacity:1;transform:none;transition:transform .18s var(--cad-ease)}.cad-dialog-container.is-drawer .cad-dialog__footer{border-radius:0}.cad-dialog-container.is-drawer.pos-right .cad-dialog{transform:translate(100%);border-width:0 0 0 1px}.cad-dialog-container.is-drawer.pos-left .cad-dialog{transform:translate(-100%);border-width:0 1px 0 0}.cad-dialog-container.is-drawer.pos-top .cad-dialog{transform:translateY(-100%);border-width:0 0 1px 0}.cad-dialog-container.is-drawer.pos-bottom .cad-dialog{transform:translateY(100%);border-width:1px 0 0 0}.cad-dialog-container.is-drawer.is-open .cad-dialog{transform:none}.cad-dialog-container.is-drawer.pos-top,.cad-dialog-container.is-drawer.pos-bottom{width:100%}.cad-dialog-container.is-drawer.pos-top .cad-dialog,.cad-dialog-container.is-drawer.pos-bottom .cad-dialog{width:100%!important;max-width:100%!important}.cad-dialog-container.is-mobile-full{width:100%;height:100dvh;max-height:100dvh}.cad-dialog-container.is-mobile-full .cad-dialog{width:100%!important;max-width:100%!important;height:100dvh!important;max-height:100dvh;border-radius:0;border:0;box-shadow:none;padding-bottom:env(safe-area-inset-bottom)}.cad-dialog-container.is-mobile-full .cad-dialog__header{position:sticky;top:0;background:var(--cad-surface);z-index:1;padding-top:calc(14px + env(safe-area-inset-top))}.cad-dialog-container.is-mobile-full .cad-dialog__footer{border-radius:0}.cad-overlay-pane.is-mobile-sheet-pane{width:100%;max-width:100%}.cad-dialog-container.is-mobile-sheet{width:100%;align-items:flex-end}.cad-dialog-container.is-mobile-sheet .cad-dialog{position:relative;width:100%!important;max-width:100%!important;height:92dvh!important;max-height:92dvh;border-radius:16px 16px 0 0;border-bottom:0;box-shadow:0 -8px 32px #0000002e;padding-bottom:env(safe-area-inset-bottom);opacity:1;transform:translateY(100%);transition:transform .2s var(--cad-ease)}.cad-dialog-container.is-mobile-sheet.sheet-h-auto .cad-dialog{height:auto!important}.cad-dialog-container.is-mobile-sheet .cad-dialog:before{content:\"\";position:absolute;top:8px;left:50%;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--cad-border-strong)}.cad-dialog-container.is-mobile-sheet .cad-dialog__header{padding-top:20px}.cad-dialog-container.is-mobile-sheet .cad-dialog__body:first-child{padding-top:22px}.cad-dialog-container.is-mobile-sheet.is-open .cad-dialog{transform:none}.cad-dialog-container.is-mobile-sheet .cad-dialog__footer{border-radius:0}.cad-confirm-dialog.is-mobile-sheet .cad-dialog__footer{flex-direction:column-reverse;align-items:stretch;gap:8px}.cad-confirm-dialog.is-mobile-sheet .cad-dialog__footer>*{width:100%;height:44px}@media(prefers-reduced-motion:reduce){.cad-dialog,.cad-dialog-container.is-drawer .cad-dialog,.cad-dialog-container.is-mobile-sheet .cad-dialog,.cdk-overlay-backdrop{transition:none!important}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i1$1.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
4152
4152
  }
4153
4153
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadDialogContainerComponent, decorators: [{
4154
4154
  type: Component,
@@ -4194,7 +4194,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
4194
4194
  <footer class="cad-dialog__footer"><ng-container *ngTemplateOutlet="config().footer!" /></footer>
4195
4195
  }
4196
4196
  </div>
4197
- `, styles: [".cdk-overlay-container{position:fixed;z-index:var(--cad-z-overlay, 100002);pointer-events:none;top:0;left:0;width:100%;height:100%}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000;width:100%;height:100%}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000;display:flex;max-width:100%;max-height:100%}.cdk-overlay-backdrop{position:absolute;inset:0;z-index:1000;pointer-events:auto;-webkit-tap-highlight-color:transparent;opacity:0;transition:opacity var(--cad-dur, .14s) var(--cad-ease, ease)}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:1}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing{visibility:visible;opacity:0}.cdk-overlay-connected-position-bounding-box{position:absolute;z-index:1000;display:flex;flex-direction:column;min-width:1px;min-height:1px}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.cad-overlay-backdrop{background:var(--cad-backdrop, rgba(2, 6, 23, .45))}.cad-overlay-backdrop.is-hidden{display:none}.cad-overlay-pane{pointer-events:auto}.cad-overlay-pane.is-drawer-pane{height:100%}.cad-dialog-container{display:flex;outline:none;font-family:var(--cad-font);font-size:var(--cad-font-size);color:var(--cad-fg);max-height:100%;max-width:100%}.cad-dialog-container.is-hidden{display:none}.cad-dialog{display:flex;flex-direction:column;background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:var(--cad-radius-lg);box-shadow:var(--cad-shadow-lg);max-height:92vh;min-width:0;outline:none;box-sizing:border-box;opacity:0;transform:translateY(6px) scale(.98);transition:opacity var(--cad-dur) var(--cad-ease),transform var(--cad-dur) var(--cad-ease)}.cad-dialog-container.is-open .cad-dialog{opacity:1;transform:none}.cad-dialog-container.is-maximized .cad-dialog{width:100vw!important;max-width:100vw!important;height:100vh!important;max-height:100vh;border-radius:0}.cad-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:14px 16px 10px;flex:none}.cad-dialog__header.is-draggable{cursor:move;-webkit-user-select:none;user-select:none}.cad-dialog__title{margin:0;font-size:14.5px;font-weight:600;color:var(--cad-fg);min-width:0}.cad-dialog__actions{display:inline-flex;gap:2px;margin-left:auto;flex:none}.cad-dialog__icon-btn{display:grid;place-items:center;width:26px;height:26px;border:0;border-radius:var(--cad-radius-sm);background:transparent;color:var(--cad-fg-muted);cursor:pointer;padding:0;transition:background var(--cad-dur),color var(--cad-dur)}.cad-dialog__icon-btn:hover{background:var(--cad-hover);color:var(--cad-fg)}.cad-dialog__icon-btn:focus-visible{outline:2px solid var(--cad-ring);outline-offset:1px}.cad-dialog__body{padding:4px 16px 14px;color:var(--cad-fg-secondary);font-size:var(--cad-font-size);overflow:auto;flex:1;min-height:0}.cad-dialog__body:first-child{padding-top:16px}.cad-dialog__footer{display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:12px 16px;border-top:1px solid var(--cad-border);background:var(--cad-bg-subtle);border-radius:0 0 var(--cad-radius-lg) var(--cad-radius-lg);flex:none}.cad-dialog-container.is-drawer{height:100%}.cad-dialog-container.is-drawer .cad-dialog{max-height:none;height:100%;border-radius:0;opacity:1;transform:none;transition:transform .18s var(--cad-ease)}.cad-dialog-container.is-drawer .cad-dialog__footer{border-radius:0}.cad-dialog-container.is-drawer.pos-right .cad-dialog{transform:translate(100%);border-width:0 0 0 1px}.cad-dialog-container.is-drawer.pos-left .cad-dialog{transform:translate(-100%);border-width:0 1px 0 0}.cad-dialog-container.is-drawer.pos-top .cad-dialog{transform:translateY(-100%);border-width:0 0 1px 0}.cad-dialog-container.is-drawer.pos-bottom .cad-dialog{transform:translateY(100%);border-width:1px 0 0 0}.cad-dialog-container.is-drawer.is-open .cad-dialog{transform:none}.cad-dialog-container.is-drawer.pos-top,.cad-dialog-container.is-drawer.pos-bottom{width:100%}.cad-dialog-container.is-drawer.pos-top .cad-dialog,.cad-dialog-container.is-drawer.pos-bottom .cad-dialog{width:100vw!important;max-width:100vw!important}.cad-dialog-container.is-mobile-full{width:100vw;height:100dvh;max-height:100dvh}.cad-dialog-container.is-mobile-full .cad-dialog{width:100vw!important;max-width:100vw!important;height:100dvh!important;max-height:100dvh;border-radius:0;border:0;box-shadow:none;padding-bottom:env(safe-area-inset-bottom)}.cad-dialog-container.is-mobile-full .cad-dialog__header{position:sticky;top:0;background:var(--cad-surface);z-index:1;padding-top:calc(14px + env(safe-area-inset-top))}.cad-dialog-container.is-mobile-full .cad-dialog__footer{border-radius:0}.cad-overlay-pane.is-mobile-sheet-pane{width:100%;max-width:100%}.cad-dialog-container.is-mobile-sheet{width:100vw;align-items:flex-end}.cad-dialog-container.is-mobile-sheet .cad-dialog{position:relative;width:100vw!important;max-width:100vw!important;height:92dvh!important;max-height:92dvh;border-radius:16px 16px 0 0;border-bottom:0;box-shadow:0 -8px 32px #0000002e;padding-bottom:env(safe-area-inset-bottom);opacity:1;transform:translateY(100%);transition:transform .2s var(--cad-ease)}.cad-dialog-container.is-mobile-sheet.sheet-h-auto .cad-dialog{height:auto!important}.cad-dialog-container.is-mobile-sheet .cad-dialog:before{content:\"\";position:absolute;top:8px;left:50%;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--cad-border-strong)}.cad-dialog-container.is-mobile-sheet .cad-dialog__header{padding-top:20px}.cad-dialog-container.is-mobile-sheet .cad-dialog__body:first-child{padding-top:22px}.cad-dialog-container.is-mobile-sheet.is-open .cad-dialog{transform:none}.cad-dialog-container.is-mobile-sheet .cad-dialog__footer{border-radius:0}.cad-confirm-dialog.is-mobile-sheet .cad-dialog__footer{flex-direction:column-reverse;align-items:stretch;gap:8px}.cad-confirm-dialog.is-mobile-sheet .cad-dialog__footer>*{width:100%;height:44px}@media(prefers-reduced-motion:reduce){.cad-dialog,.cad-dialog-container.is-drawer .cad-dialog,.cad-dialog-container.is-mobile-sheet .cad-dialog,.cdk-overlay-backdrop{transition:none!important}}\n"] }]
4197
+ `, styles: [".cdk-overlay-container{position:fixed;z-index:var(--cad-z-overlay, 100002);pointer-events:none;top:0;left:0;width:100%;height:100%}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000;width:100%;height:100%}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000;display:flex;max-width:100%;max-height:100%}.cdk-overlay-backdrop{position:absolute;inset:0;z-index:1000;pointer-events:auto;-webkit-tap-highlight-color:transparent;opacity:0;transition:opacity var(--cad-dur, .14s) var(--cad-ease, ease)}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:1}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing{visibility:visible;opacity:0}.cdk-overlay-connected-position-bounding-box{position:absolute;z-index:1000;display:flex;flex-direction:column;min-width:1px;min-height:1px}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.cad-overlay-backdrop{background:var(--cad-backdrop, rgba(2, 6, 23, .45))}.cad-overlay-backdrop.is-hidden{display:none}.cad-overlay-pane{pointer-events:auto}.cad-overlay-pane.is-drawer-pane{height:100%}.cad-dialog-container{display:flex;outline:none;font-family:var(--cad-font);font-size:var(--cad-font-size);color:var(--cad-fg);max-height:100%;max-width:100%}.cad-dialog-container.is-hidden{display:none}.cad-dialog{display:flex;flex-direction:column;background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:var(--cad-radius-lg);box-shadow:var(--cad-shadow-lg);max-height:92vh;min-width:0;outline:none;box-sizing:border-box;opacity:0;transform:translateY(6px) scale(.98);transition:opacity var(--cad-dur) var(--cad-ease),transform var(--cad-dur) var(--cad-ease)}.cad-dialog-container.is-open .cad-dialog{opacity:1;transform:none}.cad-dialog-container.is-maximized .cad-dialog{width:100%!important;max-width:100%!important;height:100vh!important;max-height:100vh;border-radius:0}.cad-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:14px 16px 10px;flex:none}.cad-dialog__header.is-draggable{cursor:move;-webkit-user-select:none;user-select:none}.cad-dialog__title{margin:0;font-size:14.5px;font-weight:600;color:var(--cad-fg);min-width:0}.cad-dialog__actions{display:inline-flex;gap:2px;margin-left:auto;flex:none}.cad-dialog__icon-btn{display:grid;place-items:center;width:26px;height:26px;border:0;border-radius:var(--cad-radius-sm);background:transparent;color:var(--cad-fg-muted);cursor:pointer;padding:0;transition:background var(--cad-dur),color var(--cad-dur)}.cad-dialog__icon-btn:hover{background:var(--cad-hover);color:var(--cad-fg)}.cad-dialog__icon-btn:focus-visible{outline:2px solid var(--cad-ring);outline-offset:1px}.cad-dialog__body{padding:4px 16px 14px;color:var(--cad-fg-secondary);font-size:var(--cad-font-size);overflow:auto;flex:1;min-height:0}.cad-dialog__body:first-child{padding-top:16px}.cad-dialog__footer{display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:12px 16px;border-top:1px solid var(--cad-border);background:var(--cad-bg-subtle);border-radius:0 0 var(--cad-radius-lg) var(--cad-radius-lg);flex:none}.cad-dialog-container.is-drawer{height:100%}.cad-dialog-container.is-drawer .cad-dialog{max-height:none;height:100%;border-radius:0;opacity:1;transform:none;transition:transform .18s var(--cad-ease)}.cad-dialog-container.is-drawer .cad-dialog__footer{border-radius:0}.cad-dialog-container.is-drawer.pos-right .cad-dialog{transform:translate(100%);border-width:0 0 0 1px}.cad-dialog-container.is-drawer.pos-left .cad-dialog{transform:translate(-100%);border-width:0 1px 0 0}.cad-dialog-container.is-drawer.pos-top .cad-dialog{transform:translateY(-100%);border-width:0 0 1px 0}.cad-dialog-container.is-drawer.pos-bottom .cad-dialog{transform:translateY(100%);border-width:1px 0 0 0}.cad-dialog-container.is-drawer.is-open .cad-dialog{transform:none}.cad-dialog-container.is-drawer.pos-top,.cad-dialog-container.is-drawer.pos-bottom{width:100%}.cad-dialog-container.is-drawer.pos-top .cad-dialog,.cad-dialog-container.is-drawer.pos-bottom .cad-dialog{width:100%!important;max-width:100%!important}.cad-dialog-container.is-mobile-full{width:100%;height:100dvh;max-height:100dvh}.cad-dialog-container.is-mobile-full .cad-dialog{width:100%!important;max-width:100%!important;height:100dvh!important;max-height:100dvh;border-radius:0;border:0;box-shadow:none;padding-bottom:env(safe-area-inset-bottom)}.cad-dialog-container.is-mobile-full .cad-dialog__header{position:sticky;top:0;background:var(--cad-surface);z-index:1;padding-top:calc(14px + env(safe-area-inset-top))}.cad-dialog-container.is-mobile-full .cad-dialog__footer{border-radius:0}.cad-overlay-pane.is-mobile-sheet-pane{width:100%;max-width:100%}.cad-dialog-container.is-mobile-sheet{width:100%;align-items:flex-end}.cad-dialog-container.is-mobile-sheet .cad-dialog{position:relative;width:100%!important;max-width:100%!important;height:92dvh!important;max-height:92dvh;border-radius:16px 16px 0 0;border-bottom:0;box-shadow:0 -8px 32px #0000002e;padding-bottom:env(safe-area-inset-bottom);opacity:1;transform:translateY(100%);transition:transform .2s var(--cad-ease)}.cad-dialog-container.is-mobile-sheet.sheet-h-auto .cad-dialog{height:auto!important}.cad-dialog-container.is-mobile-sheet .cad-dialog:before{content:\"\";position:absolute;top:8px;left:50%;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--cad-border-strong)}.cad-dialog-container.is-mobile-sheet .cad-dialog__header{padding-top:20px}.cad-dialog-container.is-mobile-sheet .cad-dialog__body:first-child{padding-top:22px}.cad-dialog-container.is-mobile-sheet.is-open .cad-dialog{transform:none}.cad-dialog-container.is-mobile-sheet .cad-dialog__footer{border-radius:0}.cad-confirm-dialog.is-mobile-sheet .cad-dialog__footer{flex-direction:column-reverse;align-items:stretch;gap:8px}.cad-confirm-dialog.is-mobile-sheet .cad-dialog__footer>*{width:100%;height:44px}@media(prefers-reduced-motion:reduce){.cad-dialog,.cad-dialog-container.is-drawer .cad-dialog,.cad-dialog-container.is-mobile-sheet .cad-dialog,.cdk-overlay-backdrop{transition:none!important}}\n"] }]
4198
4198
  }], propDecorators: { closeRequest: [{ type: i0.Output, args: ["closeRequest"] }], attached: [{ type: i0.Output, args: ["attached"] }], maximizeChange: [{ type: i0.Output, args: ["maximizeChange"] }] } });
4199
4199
 
4200
4200
  const ANIM_MS = 180;
@@ -6004,7 +6004,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
6004
6004
  }], ctorParameters: () => [], propDecorators: { cadIf: [{ type: i0.Input, args: [{ isSignal: true, alias: "cadIf", required: true }] }] } });
6005
6005
 
6006
6006
  const J = { start: 'flex-start', center: 'center', end: 'flex-end', between: 'space-between', around: 'space-around', evenly: 'space-evenly' };
6007
- const A = { stretch: 'stretch', start: 'flex-start', center: 'center', end: 'flex-end', baseline: 'baseline' };
6007
+ const A$1 = { stretch: 'stretch', start: 'flex-start', center: 'center', end: 'flex-end', baseline: 'baseline' };
6008
6008
  /** Pile verticale : `<cad-stack gap="sm" align="stretch">` — les enfants s'empilent avec un gap uniforme (jamais de marges). */
6009
6009
  class CadStackComponent {
6010
6010
  gap = input('md', ...(ngDevMode ? [{ debugName: "gap" }] : /* istanbul ignore next */ []));
@@ -6012,7 +6012,7 @@ class CadStackComponent {
6012
6012
  justify = input('start', ...(ngDevMode ? [{ debugName: "justify" }] : /* istanbul ignore next */ []));
6013
6013
  inline = input(false, { ...(ngDevMode ? { debugName: "inline" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
6014
6014
  g = computed(() => cadGap(this.gap()), ...(ngDevMode ? [{ debugName: "g" }] : /* istanbul ignore next */ []));
6015
- a = computed(() => A[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6015
+ a = computed(() => A$1[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6016
6016
  j = computed(() => J[this.justify()], ...(ngDevMode ? [{ debugName: "j" }] : /* istanbul ignore next */ []));
6017
6017
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadStackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6018
6018
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.20", type: CadStackComponent, isStandalone: true, selector: "cad-stack", inputs: { gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: true, isRequired: false, transformFunction: null }, inline: { classPropertyName: "inline", publicName: "inline", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.gap": "g()", "style.align-items": "a()", "style.justify-content": "j()", "class.is-inline": "inline()" }, classAttribute: "cad-stack" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:flex;flex-direction:column;min-width:0}:host(.is-inline){display:inline-flex}:host ::ng-deep>*{min-width:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
@@ -6029,7 +6029,7 @@ class CadClusterComponent {
6029
6029
  wrap = input(true, { ...(ngDevMode ? { debugName: "wrap" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
6030
6030
  inline = input(false, { ...(ngDevMode ? { debugName: "inline" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
6031
6031
  g = computed(() => cadGap(this.gap()), ...(ngDevMode ? [{ debugName: "g" }] : /* istanbul ignore next */ []));
6032
- a = computed(() => A[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6032
+ a = computed(() => A$1[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6033
6033
  j = computed(() => J[this.justify()], ...(ngDevMode ? [{ debugName: "j" }] : /* istanbul ignore next */ []));
6034
6034
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadClusterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6035
6035
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.20", type: CadClusterComponent, isStandalone: true, selector: "cad-cluster", inputs: { gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null }, inline: { classPropertyName: "inline", publicName: "inline", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.gap": "g()", "style.align-items": "a()", "style.justify-content": "j()", "style.flex-wrap": "wrap() ? \"wrap\" : \"nowrap\"", "class.is-inline": "inline()" }, classAttribute: "cad-cluster" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:flex;min-width:0}:host(.is-inline){display:inline-flex}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
@@ -6054,7 +6054,7 @@ class CadGridComponent {
6054
6054
  tpl = computed(() => this.min() ? `repeat(auto-fit, minmax(min(${this.min()}, 100%), 1fr))` : `repeat(${this.c() ?? 1}, minmax(0, 1fr))`, ...(ngDevMode ? [{ debugName: "tpl" }] : /* istanbul ignore next */ []));
6055
6055
  g = computed(() => cadGap(this.gap()), ...(ngDevMode ? [{ debugName: "g" }] : /* istanbul ignore next */ []));
6056
6056
  rg = computed(() => this.rowGap() ? cadGap(this.rowGap()) : null, ...(ngDevMode ? [{ debugName: "rg" }] : /* istanbul ignore next */ []));
6057
- a = computed(() => A[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6057
+ a = computed(() => A$1[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6058
6058
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6059
6059
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.20", type: CadGridComponent, isStandalone: true, selector: "cad-grid", inputs: { cols: { classPropertyName: "cols", publicName: "cols", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, rowGap: { classPropertyName: "rowGap", publicName: "rowGap", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, dense: { classPropertyName: "dense", publicName: "dense", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.grid-template-columns": "tpl()", "style.gap": "g()", "style.row-gap": "rg()", "style.align-items": "a()", "style.grid-auto-flow": "dense() ? \"row dense\" : null" }, classAttribute: "cad-grid" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:grid;min-width:0}:host ::ng-deep>*{min-width:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6060
6060
  }
@@ -6083,7 +6083,7 @@ class CadSplitComponent {
6083
6083
  stackAt = input('sm', ...(ngDevMode ? [{ debugName: "stackAt" }] : /* istanbul ignore next */ []));
6084
6084
  tpl = computed(() => (this.stackAt() && this.bp.down(this.stackAt())) ? '1fr' : this.ratio(), ...(ngDevMode ? [{ debugName: "tpl" }] : /* istanbul ignore next */ []));
6085
6085
  g = computed(() => cadGap(this.gap()), ...(ngDevMode ? [{ debugName: "g" }] : /* istanbul ignore next */ []));
6086
- a = computed(() => A[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6086
+ a = computed(() => A$1[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6087
6087
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadSplitComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6088
6088
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.20", type: CadSplitComponent, isStandalone: true, selector: "cad-split", inputs: { ratio: { classPropertyName: "ratio", publicName: "ratio", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, stackAt: { classPropertyName: "stackAt", publicName: "stackAt", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.grid-template-columns": "tpl()", "style.gap": "g()", "style.align-items": "a()" }, classAttribute: "cad-split" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:grid;min-width:0}:host ::ng-deep>*{min-width:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6089
6089
  }
@@ -6114,7 +6114,7 @@ class CadSidebarComponent {
6114
6114
  collapseAt = input('', ...(ngDevMode ? [{ debugName: "collapseAt" }] : /* istanbul ignore next */ []));
6115
6115
  collapsed = computed(() => !!this.collapseAt() && this.bp.down(this.collapseAt()), ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
6116
6116
  g = computed(() => cadGap(this.gap()), ...(ngDevMode ? [{ debugName: "g" }] : /* istanbul ignore next */ []));
6117
- a = computed(() => A[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6117
+ a = computed(() => A$1[this.align()], ...(ngDevMode ? [{ debugName: "a" }] : /* istanbul ignore next */ []));
6118
6118
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6119
6119
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.20", type: CadSidebarComponent, isStandalone: true, selector: "cad-sidebar", inputs: { side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, contentMin: { classPropertyName: "contentMin", publicName: "contentMin", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, collapseAt: { classPropertyName: "collapseAt", publicName: "collapseAt", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.gap": "g()", "class.side-left": "side() === \"left\"", "class.is-collapsed": "collapsed()", "style.--cad-sb-w": "width()", "style.--cad-sb-min": "contentMin()", "style.align-items": "a()" }, classAttribute: "cad-sidebar" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:flex;flex-wrap:wrap;min-width:0}:host ::ng-deep>*{flex-basis:0;flex-grow:999;min-inline-size:var(--cad-sb-min, 60%);min-width:0}:host ::ng-deep>.cad-sidebar__panel{flex-basis:var(--cad-sb-w, 280px);flex-grow:1;min-inline-size:0}:host(.side-left) ::ng-deep>.cad-sidebar__panel{order:-1}:host(.is-collapsed) ::ng-deep>*{flex-basis:100%!important;min-inline-size:0!important}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6120
6120
  }
@@ -6281,6 +6281,108 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
6281
6281
  `, styles: [":host{display:grid;width:100%;height:100%;min-height:0;min-width:0;--_g: 6px}:host ::ng-deep>cad-splitter-panel{grid-row:1}:host(.is-vertical) ::ng-deep>cad-splitter-panel{grid-row:auto;grid-column:1}:host(:not(.is-vertical)) ::ng-deep>cad-splitter-panel:nth-child(1){grid-column:1}:host(:not(.is-vertical)) ::ng-deep>cad-splitter-panel:nth-child(2){grid-column:3}:host(:not(.is-vertical)) ::ng-deep>cad-splitter-panel:nth-child(3){grid-column:5}:host(:not(.is-vertical)) ::ng-deep>cad-splitter-panel:nth-child(4){grid-column:7}:host(:not(.is-vertical)) ::ng-deep>cad-splitter-panel:nth-child(5){grid-column:9}:host(.is-vertical) ::ng-deep>cad-splitter-panel:nth-child(1){grid-row:1}:host(.is-vertical) ::ng-deep>cad-splitter-panel:nth-child(2){grid-row:3}:host(.is-vertical) ::ng-deep>cad-splitter-panel:nth-child(3){grid-row:5}:host(.is-vertical) ::ng-deep>cad-splitter-panel:nth-child(4){grid-row:7}:host(.is-vertical) ::ng-deep>cad-splitter-panel:nth-child(5){grid-row:9}.sp__gutter{grid-row:1;background:var(--cad-border);display:grid;place-items:center;cursor:col-resize;touch-action:none;-webkit-user-select:none;user-select:none;transition:background var(--cad-dur)}:host(.is-vertical) .sp__gutter{grid-column:1;cursor:row-resize}.sp__gutter i{width:2px;height:22px;border-radius:2px;background:var(--cad-border-strong)}:host(.is-vertical) .sp__gutter i{width:22px;height:2px}.sp__gutter:hover,.sp__gutter.is-active{background:var(--cad-accent-soft)}.sp__gutter:hover i,.sp__gutter.is-active i{background:var(--cad-accent)}.sp__gutter:focus-visible{outline:2px solid var(--cad-ring);outline-offset:-2px}\n"] }]
6282
6282
  }], ctorParameters: () => [], propDecorators: { layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], panelSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelSizes", required: false }] }], minSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSizes", required: false }] }], gutterSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterSize", required: false }] }], stateKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateKey", required: false }] }], resizeStart: [{ type: i0.Output, args: ["resizeStart"] }], resizeEnd: [{ type: i0.Output, args: ["resizeEnd"] }], panels: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CadSplitterPanelComponent), { isSignal: true }] }] } });
6283
6283
 
6284
+ const CAD_SHELL = new InjectionToken('CAD_SHELL');
6285
+ /**
6286
+ * Coquille d'application : barre haute, menu, contenu, barre basse.
6287
+ *
6288
+ * POURQUOI ELLE EXISTE — la bibliothèque avait `cad-bottom-nav`, `cad-menubar`,
6289
+ * `cad-nav-drawer` et `cad-sidebar`, mais aucune coquille. Chaque application refaisait donc
6290
+ * sa mise en page, et retombait sur le même défaut, signalé par SZU le 21/08/2026 : la barre
6291
+ * du haut « défile au lieu de rester en place ». Ce n'était pas la barre qui bougeait, c'était
6292
+ * le DOCUMENT qui défilait.
6293
+ *
6294
+ * LE PARTI PRIS — on ne fixe pas les barres une par une (`position: fixed`), on rend la
6295
+ * coquille INCAPABLE de défiler : hauteur exacte du viewport, seul le contenu défile. Les
6296
+ * barres restent en place parce qu'elles ne peuvent pas bouger. Rien à réserver dans le
6297
+ * contenu, aucune zone sûre dupliquée, un seul seuil. `fixed` reste une porte de sortie pour
6298
+ * un cas particulier, pas le mécanisme.
6299
+ *
6300
+ * AGENCEMENT — barre haute sur TOUTE la largeur, menu dessous : c'est celui du template
6301
+ * CEREMA (`_topbar.scss` : `position: fixed; top: 0; width: 100%`), et une bibliothèque ne
6302
+ * doit pas imposer l'inverse de ce que font les applications maison.
6303
+ *
6304
+ * ELLE MESURE LE MENU, ELLE NE LE POSSÈDE PAS (arbitrage SZU : « le tiroir c'est le composant
6305
+ * menu lui-même »). Elle fonctionne donc sans menu (`--cad-shell-nav: 0`), n'exige aucun
6306
+ * enfant, et `openNav()` se contente d'émettre `navRequested` — c'est l'hôte qui ouvre son
6307
+ * `cad-nav-drawer`. Aucun couplage dans un sens ni dans l'autre.
6308
+ *
6309
+ * ```html
6310
+ * <cad-app-shell (navRequested)="menu.set(true)">
6311
+ * <header cadShellBar>…</header>
6312
+ * <cad-nav-drawer cadShellNav [items]="menu" [(open)]="ouvert"/>
6313
+ * <router-outlet />
6314
+ * <cad-bottom-nav cadShellBottom [items]="dest" [(value)]="page"/>
6315
+ * </cad-app-shell>
6316
+ * ```
6317
+ */
6318
+ class CadAppShellComponent {
6319
+ bp = inject(CadBreakpointService);
6320
+ /** Surcharge locale du seuil. Par defaut : celui de la bibliotheque (CAD_MOBILE_BREAKPOINT). */
6321
+ compactAt = input(null, ...(ngDevMode ? [{ debugName: "compactAt" }] : /* istanbul ignore next */ []));
6322
+ /** Un enfant a demande le menu (bouton hamburger de la barre, par exemple). */
6323
+ navRequested = output();
6324
+ barRef = viewChild.required('bar');
6325
+ navRef = viewChild.required('nav');
6326
+ bottomRef = viewChild.required('bottom');
6327
+ _top = signal(0, ...(ngDevMode ? [{ debugName: "_top" }] : /* istanbul ignore next */ []));
6328
+ _bottom = signal(0, ...(ngDevMode ? [{ debugName: "_bottom" }] : /* istanbul ignore next */ []));
6329
+ _nav = signal(0, ...(ngDevMode ? [{ debugName: "_nav" }] : /* istanbul ignore next */ []));
6330
+ topHeight = this._top.asReadonly();
6331
+ bottomHeight = this._bottom.asReadonly();
6332
+ navWidth = this._nav.asReadonly();
6333
+ compact = computed(() => {
6334
+ const seuil = this.compactAt();
6335
+ return seuil == null ? this.bp.isMobile() : this.bp.width() < seuil;
6336
+ }, ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
6337
+ constructor() {
6338
+ // Les hauteurs sont MESURÉES, jamais déclarées : une barre dont le contenu grandit
6339
+ // (deux lignes de titre, un bandeau d'alerte) doit rester juste sans que personne
6340
+ // n'ait à mettre un chiffre à jour quelque part.
6341
+ effect((onCleanup) => {
6342
+ if (typeof ResizeObserver === 'undefined')
6343
+ return;
6344
+ const cibles = [
6345
+ [this.barRef().nativeElement, (n) => this._top.set(n), 'h'],
6346
+ [this.bottomRef().nativeElement, (n) => this._bottom.set(n), 'h'],
6347
+ [this.navRef().nativeElement, (n) => this._nav.set(n), 'w'],
6348
+ ];
6349
+ const ro = new ResizeObserver(() => {
6350
+ for (const [el, set, axe] of cibles) {
6351
+ const r = el.getBoundingClientRect();
6352
+ set(Math.round(axe === 'h' ? r.height : r.width));
6353
+ }
6354
+ });
6355
+ for (const [el] of cibles)
6356
+ ro.observe(el);
6357
+ onCleanup(() => ro.disconnect());
6358
+ });
6359
+ inject(DestroyRef).onDestroy(() => { });
6360
+ }
6361
+ openNav() { this.navRequested.emit(); }
6362
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadAppShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6363
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.20", type: CadAppShellComponent, isStandalone: true, selector: "cad-app-shell", inputs: { compactAt: { classPropertyName: "compactAt", publicName: "compactAt", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navRequested: "navRequested" }, host: { properties: { "class.is-compact": "compact()", "style.--cad-shell-top.px": "topHeight()", "style.--cad-shell-bottom.px": "bottomHeight()", "style.--cad-shell-nav.px": "navWidth()" }, classAttribute: "cad-app-shell" }, providers: [{ provide: CAD_SHELL, useExisting: CadAppShellComponent }], viewQueries: [{ propertyName: "barRef", first: true, predicate: ["bar"], descendants: true, isSignal: true }, { propertyName: "navRef", first: true, predicate: ["nav"], descendants: true, isSignal: true }, { propertyName: "bottomRef", first: true, predicate: ["bottom"], descendants: true, isSignal: true }], ngImport: i0, template: `
6364
+ <div class="cad-shell__bar" #bar><ng-content select="[cadShellBar]" /></div>
6365
+ <div class="cad-shell__nav" #nav><ng-content select="[cadShellNav]" /></div>
6366
+ <div class="cad-shell__main" #main><ng-content /></div>
6367
+ <div class="cad-shell__bottom" #bottom><ng-content select="[cadShellBottom]" /></div>
6368
+ `, isInline: true, styles: [":host{display:grid;height:100dvh;overflow:hidden;grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;background:var(--cad-bg);color:var(--cad-fg);font-family:var(--cad-font);font-size:var(--cad-font-size)}.cad-shell__bar{grid-column:1 / -1;grid-row:1;min-width:0}.cad-shell__nav{grid-column:1;grid-row:2;min-height:0;display:flex}.cad-shell__main{grid-column:2;grid-row:2;min-width:0;min-height:0;overflow-y:auto;overscroll-behavior:contain}.cad-shell__bottom{grid-column:1 / -1;grid-row:3;min-width:0}.cad-shell__bar:empty,.cad-shell__nav:empty,.cad-shell__bottom:empty{display:none}:host(.is-compact) .cad-shell__nav{width:0;overflow:visible}:host(.is-compact) .cad-shell__main{grid-column:1 / -1}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6369
+ }
6370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadAppShellComponent, decorators: [{
6371
+ type: Component,
6372
+ args: [{ selector: 'cad-app-shell', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: CAD_SHELL, useExisting: CadAppShellComponent }], host: {
6373
+ class: 'cad-app-shell',
6374
+ '[class.is-compact]': 'compact()',
6375
+ '[style.--cad-shell-top.px]': 'topHeight()',
6376
+ '[style.--cad-shell-bottom.px]': 'bottomHeight()',
6377
+ '[style.--cad-shell-nav.px]': 'navWidth()',
6378
+ }, template: `
6379
+ <div class="cad-shell__bar" #bar><ng-content select="[cadShellBar]" /></div>
6380
+ <div class="cad-shell__nav" #nav><ng-content select="[cadShellNav]" /></div>
6381
+ <div class="cad-shell__main" #main><ng-content /></div>
6382
+ <div class="cad-shell__bottom" #bottom><ng-content select="[cadShellBottom]" /></div>
6383
+ `, styles: [":host{display:grid;height:100dvh;overflow:hidden;grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;background:var(--cad-bg);color:var(--cad-fg);font-family:var(--cad-font);font-size:var(--cad-font-size)}.cad-shell__bar{grid-column:1 / -1;grid-row:1;min-width:0}.cad-shell__nav{grid-column:1;grid-row:2;min-height:0;display:flex}.cad-shell__main{grid-column:2;grid-row:2;min-width:0;min-height:0;overflow-y:auto;overscroll-behavior:contain}.cad-shell__bottom{grid-column:1 / -1;grid-row:3;min-width:0}.cad-shell__bar:empty,.cad-shell__nav:empty,.cad-shell__bottom:empty{display:none}:host(.is-compact) .cad-shell__nav{width:0;overflow:visible}:host(.is-compact) .cad-shell__main{grid-column:1 / -1}\n"] }]
6384
+ }], ctorParameters: () => [], propDecorators: { compactAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "compactAt", required: false }] }], navRequested: [{ type: i0.Output, args: ["navRequested"] }], barRef: [{ type: i0.ViewChild, args: ['bar', { isSignal: true }] }], navRef: [{ type: i0.ViewChild, args: ['nav', { isSignal: true }] }], bottomRef: [{ type: i0.ViewChild, args: ['bottom', { isSignal: true }] }] } });
6385
+
6284
6386
  /**
6285
6387
  * Formulaire de modification d'une ligne (editMode="row") ouvert dans un cad-dialog par la table :
6286
6388
  * un champ par colonne `editable` (éditeur natif de la colonne ou `cadEditorTemplate`), validation `validator`,
@@ -9182,7 +9284,7 @@ class CadBottomNavComponent {
9182
9284
  </span>
9183
9285
  @if (showLabel(it)) {<span class="cad-bottom-nav__label">{{ it.label }}</span>}
9184
9286
  </ng-template>
9185
- `, isInline: true, styles: [":host{display:block;position:sticky;bottom:0;z-index:20;background:var(--cad-overlay);border-top:1px solid var(--cad-border);padding-bottom:env(safe-area-inset-bottom,0px)}:host(.is-hidden){display:none}.cad-bottom-nav__list{display:flex;margin:0;padding:4px 4px 2px;list-style:none}.cad-bottom-nav__list>li{flex:1;min-width:0;display:flex}.cad-bottom-nav__item{all:unset;box-sizing:border-box;flex:1;min-width:0;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-height:44px;padding:5px 2px 6px;color:var(--cad-fg-muted);font:10px var(--cad-font);text-align:center;text-decoration:none;border-radius:var(--cad-radius);position:relative}.cad-bottom-nav__item:hover{color:var(--cad-fg-secondary)}.cad-bottom-nav__item:focus-visible{outline:2px solid var(--cad-accent);outline-offset:-2px}.cad-bottom-nav__item.is-active{color:var(--cad-accent)}.cad-bottom-nav__item.is-disabled,.cad-bottom-nav__item:disabled{opacity:.45;cursor:not-allowed}.cad-bottom-nav__icon{position:relative;display:grid;place-items:center;width:100%;height:26px}.cad-bottom-nav__pill{position:absolute;width:38px;height:26px;border-radius:var(--cad-radius-pill);background:transparent;transition:background var(--cad-dur) var(--cad-ease)}.cad-bottom-nav__item.is-active .cad-bottom-nav__pill{background:var(--cad-accent-soft)}.cad-bottom-nav__icon cad-icon{position:relative;z-index:1}.cad-bottom-nav__label{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3;position:relative;z-index:1}.cad-bottom-nav__item.is-active .cad-bottom-nav__label{font-weight:500}.cad-bottom-nav__badge{position:absolute;top:-2px;left:50%;margin-left:6px;z-index:2;min-width:15px;height:15px;padding:0 4px;border-radius:var(--cad-radius-pill);background:var(--cad-danger, #f85149);color:#fff;font-size:9px;font-weight:600;line-height:15px;text-align:center;border:2px solid var(--cad-overlay);box-sizing:content-box}.cad-bottom-nav__badge.is-dot{min-width:8px;height:8px;padding:0;margin-left:8px}:host-context(.cad-touch) .cad-bottom-nav__item{min-height:52px}@media(prefers-reduced-motion:reduce){.cad-bottom-nav__pill{transition:none}}\n"], dependencies: [{ kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9287
+ `, isInline: true, styles: [":host{display:block;position:sticky;bottom:0;z-index:20;background:var(--cad-overlay);border-top:1px solid var(--cad-border);padding-bottom:var(--cad-safe-bottom, env(safe-area-inset-bottom, 0px))}:host(.is-hidden){display:none}.cad-bottom-nav__list{display:flex;margin:0;padding:4px 4px 2px;list-style:none}.cad-bottom-nav__list>li{flex:1;min-width:0;display:flex}.cad-bottom-nav__item{all:unset;box-sizing:border-box;flex:1;min-width:0;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-height:44px;padding:5px 2px 6px;color:var(--cad-fg-muted);font:10px var(--cad-font);text-align:center;text-decoration:none;border-radius:var(--cad-radius);position:relative}.cad-bottom-nav__item:hover{color:var(--cad-fg-secondary)}.cad-bottom-nav__item:focus-visible{outline:2px solid var(--cad-accent);outline-offset:-2px}.cad-bottom-nav__item.is-active{color:var(--cad-accent)}.cad-bottom-nav__item.is-disabled,.cad-bottom-nav__item:disabled{opacity:.45;cursor:not-allowed}.cad-bottom-nav__icon{position:relative;display:grid;place-items:center;width:100%;height:26px}.cad-bottom-nav__pill{position:absolute;width:38px;height:26px;border-radius:var(--cad-radius-pill);background:transparent;transition:background var(--cad-dur) var(--cad-ease)}.cad-bottom-nav__item.is-active .cad-bottom-nav__pill{background:var(--cad-accent-soft)}.cad-bottom-nav__icon cad-icon{position:relative;z-index:1}.cad-bottom-nav__label{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3;position:relative;z-index:1}.cad-bottom-nav__item.is-active .cad-bottom-nav__label{font-weight:500}.cad-bottom-nav__badge{position:absolute;top:-2px;left:50%;margin-left:6px;z-index:2;min-width:15px;height:15px;padding:0 4px;border-radius:var(--cad-radius-pill);background:var(--cad-danger, #f85149);color:#fff;font-size:9px;font-weight:600;line-height:15px;text-align:center;border:2px solid var(--cad-overlay);box-sizing:content-box}.cad-bottom-nav__badge.is-dot{min-width:8px;height:8px;padding:0;margin-left:8px}:host-context(.cad-touch) .cad-bottom-nav__item{min-height:52px}@media(prefers-reduced-motion:reduce){.cad-bottom-nav__pill{transition:none}}\n"], dependencies: [{ kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9186
9288
  }
9187
9289
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadBottomNavComponent, decorators: [{
9188
9290
  type: Component,
@@ -9248,7 +9350,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
9248
9350
  </span>
9249
9351
  @if (showLabel(it)) {<span class="cad-bottom-nav__label">{{ it.label }}</span>}
9250
9352
  </ng-template>
9251
- `, styles: [":host{display:block;position:sticky;bottom:0;z-index:20;background:var(--cad-overlay);border-top:1px solid var(--cad-border);padding-bottom:env(safe-area-inset-bottom,0px)}:host(.is-hidden){display:none}.cad-bottom-nav__list{display:flex;margin:0;padding:4px 4px 2px;list-style:none}.cad-bottom-nav__list>li{flex:1;min-width:0;display:flex}.cad-bottom-nav__item{all:unset;box-sizing:border-box;flex:1;min-width:0;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-height:44px;padding:5px 2px 6px;color:var(--cad-fg-muted);font:10px var(--cad-font);text-align:center;text-decoration:none;border-radius:var(--cad-radius);position:relative}.cad-bottom-nav__item:hover{color:var(--cad-fg-secondary)}.cad-bottom-nav__item:focus-visible{outline:2px solid var(--cad-accent);outline-offset:-2px}.cad-bottom-nav__item.is-active{color:var(--cad-accent)}.cad-bottom-nav__item.is-disabled,.cad-bottom-nav__item:disabled{opacity:.45;cursor:not-allowed}.cad-bottom-nav__icon{position:relative;display:grid;place-items:center;width:100%;height:26px}.cad-bottom-nav__pill{position:absolute;width:38px;height:26px;border-radius:var(--cad-radius-pill);background:transparent;transition:background var(--cad-dur) var(--cad-ease)}.cad-bottom-nav__item.is-active .cad-bottom-nav__pill{background:var(--cad-accent-soft)}.cad-bottom-nav__icon cad-icon{position:relative;z-index:1}.cad-bottom-nav__label{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3;position:relative;z-index:1}.cad-bottom-nav__item.is-active .cad-bottom-nav__label{font-weight:500}.cad-bottom-nav__badge{position:absolute;top:-2px;left:50%;margin-left:6px;z-index:2;min-width:15px;height:15px;padding:0 4px;border-radius:var(--cad-radius-pill);background:var(--cad-danger, #f85149);color:#fff;font-size:9px;font-weight:600;line-height:15px;text-align:center;border:2px solid var(--cad-overlay);box-sizing:content-box}.cad-bottom-nav__badge.is-dot{min-width:8px;height:8px;padding:0;margin-left:8px}:host-context(.cad-touch) .cad-bottom-nav__item{min-height:52px}@media(prefers-reduced-motion:reduce){.cad-bottom-nav__pill{transition:none}}\n"] }]
9353
+ `, styles: [":host{display:block;position:sticky;bottom:0;z-index:20;background:var(--cad-overlay);border-top:1px solid var(--cad-border);padding-bottom:var(--cad-safe-bottom, env(safe-area-inset-bottom, 0px))}:host(.is-hidden){display:none}.cad-bottom-nav__list{display:flex;margin:0;padding:4px 4px 2px;list-style:none}.cad-bottom-nav__list>li{flex:1;min-width:0;display:flex}.cad-bottom-nav__item{all:unset;box-sizing:border-box;flex:1;min-width:0;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-height:44px;padding:5px 2px 6px;color:var(--cad-fg-muted);font:10px var(--cad-font);text-align:center;text-decoration:none;border-radius:var(--cad-radius);position:relative}.cad-bottom-nav__item:hover{color:var(--cad-fg-secondary)}.cad-bottom-nav__item:focus-visible{outline:2px solid var(--cad-accent);outline-offset:-2px}.cad-bottom-nav__item.is-active{color:var(--cad-accent)}.cad-bottom-nav__item.is-disabled,.cad-bottom-nav__item:disabled{opacity:.45;cursor:not-allowed}.cad-bottom-nav__icon{position:relative;display:grid;place-items:center;width:100%;height:26px}.cad-bottom-nav__pill{position:absolute;width:38px;height:26px;border-radius:var(--cad-radius-pill);background:transparent;transition:background var(--cad-dur) var(--cad-ease)}.cad-bottom-nav__item.is-active .cad-bottom-nav__pill{background:var(--cad-accent-soft)}.cad-bottom-nav__icon cad-icon{position:relative;z-index:1}.cad-bottom-nav__label{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3;position:relative;z-index:1}.cad-bottom-nav__item.is-active .cad-bottom-nav__label{font-weight:500}.cad-bottom-nav__badge{position:absolute;top:-2px;left:50%;margin-left:6px;z-index:2;min-width:15px;height:15px;padding:0 4px;border-radius:var(--cad-radius-pill);background:var(--cad-danger, #f85149);color:#fff;font-size:9px;font-weight:600;line-height:15px;text-align:center;border:2px solid var(--cad-overlay);box-sizing:content-box}.cad-bottom-nav__badge.is-dot{min-width:8px;height:8px;padding:0;margin-left:8px}:host-context(.cad-touch) .cad-bottom-nav__item{min-height:52px}@media(prefers-reduced-motion:reduce){.cad-bottom-nav__pill{transition:none}}\n"] }]
9252
9354
  }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], maxItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxItems", required: false }] }], breakpoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "breakpoint", required: false }] }], alwaysShow: [{ type: i0.Input, args: [{ isSignal: true, alias: "alwaysShow", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }] } });
9253
9355
 
9254
9356
  /**
@@ -12274,7 +12376,7 @@ class CadToastComponent {
12274
12376
  }
12275
12377
  </div>
12276
12378
  }
12277
- `, isInline: true, styles: [":host{position:fixed;display:flex;flex-direction:column;gap:8px;width:340px;max-width:calc(100vw - 32px);pointer-events:none;font-family:var(--cad-font)}:host(.pos-top-right){top:16px;right:16px}:host(.pos-top-left){top:16px;left:16px}:host(.pos-bottom-right){bottom:16px;right:16px;flex-direction:column-reverse}:host(.pos-bottom-left){bottom:16px;left:16px;flex-direction:column-reverse}:host(.pos-top-center){top:16px;left:50%;transform:translate(-50%)}:host(.pos-bottom-center){bottom:16px;left:50%;transform:translate(-50%);flex-direction:column-reverse}.cad-toast__item{--_c: var(--cad-accent);position:relative;overflow:hidden;pointer-events:auto;display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border-radius:10px;background:var(--cad-overlay);border:1px solid var(--cad-border);border-left:3px solid var(--_c);box-shadow:var(--cad-shadow-lg);color:var(--cad-fg);animation:cad-toast-in .18s var(--cad-ease)}.cad-toast__item.is-out{animation:cad-toast-out .16s var(--cad-ease) forwards}:host(.pos-top-left) .cad-toast__item,:host(.pos-bottom-left) .cad-toast__item{animation-name:cad-toast-in-l}.cad-toast__item.sev-success{--_c: var(--cad-success)}.cad-toast__item.sev-info{--_c: var(--cad-info)}.cad-toast__item.sev-warning{--_c: var(--cad-warning)}.cad-toast__item.sev-danger{--_c: var(--cad-danger)}.cad-toast__item.sev-secondary{--_c: var(--cad-fg-muted)}.cad-toast__item.sev-contrast{--_c: var(--cad-fg);background:var(--cad-fg);color:var(--cad-bg)}.cad-toast__icon{color:var(--_c);margin-top:1px;flex:none}.cad-toast__text{display:grid;gap:2px;flex:1;min-width:0}.cad-toast__text b{font-weight:600;font-size:13px}.cad-toast__text span{color:var(--cad-fg-secondary);font-size:12.5px;line-height:1.4}.sev-contrast .cad-toast__text span{color:inherit;opacity:.8}.cad-toast__actions{display:flex;gap:4px;margin-top:4px;margin-left:-8px}.cad-toast__close{border:0;background:transparent;color:var(--cad-fg-muted);cursor:pointer;padding:2px;border-radius:4px;display:grid;place-items:center;flex:none;margin:-2px -4px 0 0}.cad-toast__close:hover{color:var(--cad-fg);background:var(--cad-hover)}.cad-toast__progress{position:absolute;left:0;bottom:0;height:2px;width:100%;background:var(--_c);opacity:.5;transform-origin:left;animation:cad-toast-life linear forwards}@keyframes cad-toast-in{0%{opacity:0;transform:translate(24px)}to{opacity:1;transform:none}}@keyframes cad-toast-in-l{0%{opacity:0;transform:translate(-24px)}to{opacity:1;transform:none}}@keyframes cad-toast-out{to{opacity:0;transform:scale(.96)}}@keyframes cad-toast-life{0%{transform:scaleX(1)}to{transform:scaleX(0)}}@media(prefers-reduced-motion:reduce){.cad-toast__item,.cad-toast__item.is-out{animation-duration:1ms}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }, { kind: "component", type: CadButtonComponent, selector: "button[cadButton], a[cadButton]", inputs: ["severity", "variant", "size", "loading", "disabled", "icon", "block", "value", "pressed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
12379
+ `, isInline: true, styles: [":host{position:fixed;display:flex;flex-direction:column;gap:8px;width:340px;max-width:calc(100% - 32px);pointer-events:none;font-family:var(--cad-font)}:host(.pos-top-right){top:16px;right:16px}:host(.pos-top-left){top:16px;left:16px}:host(.pos-bottom-right){bottom:16px;right:16px;flex-direction:column-reverse}:host(.pos-bottom-left){bottom:16px;left:16px;flex-direction:column-reverse}:host(.pos-top-center){top:16px;left:50%;transform:translate(-50%)}:host(.pos-bottom-center){bottom:16px;left:50%;transform:translate(-50%);flex-direction:column-reverse}.cad-toast__item{--_c: var(--cad-accent);position:relative;overflow:hidden;pointer-events:auto;display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border-radius:10px;background:var(--cad-overlay);border:1px solid var(--cad-border);border-left:3px solid var(--_c);box-shadow:var(--cad-shadow-lg);color:var(--cad-fg);animation:cad-toast-in .18s var(--cad-ease)}.cad-toast__item.is-out{animation:cad-toast-out .16s var(--cad-ease) forwards}:host(.pos-top-left) .cad-toast__item,:host(.pos-bottom-left) .cad-toast__item{animation-name:cad-toast-in-l}.cad-toast__item.sev-success{--_c: var(--cad-success)}.cad-toast__item.sev-info{--_c: var(--cad-info)}.cad-toast__item.sev-warning{--_c: var(--cad-warning)}.cad-toast__item.sev-danger{--_c: var(--cad-danger)}.cad-toast__item.sev-secondary{--_c: var(--cad-fg-muted)}.cad-toast__item.sev-contrast{--_c: var(--cad-fg);background:var(--cad-fg);color:var(--cad-bg)}.cad-toast__icon{color:var(--_c);margin-top:1px;flex:none}.cad-toast__text{display:grid;gap:2px;flex:1;min-width:0}.cad-toast__text b{font-weight:600;font-size:13px}.cad-toast__text span{color:var(--cad-fg-secondary);font-size:12.5px;line-height:1.4}.sev-contrast .cad-toast__text span{color:inherit;opacity:.8}.cad-toast__actions{display:flex;gap:4px;margin-top:4px;margin-left:-8px}.cad-toast__close{border:0;background:transparent;color:var(--cad-fg-muted);cursor:pointer;padding:2px;border-radius:4px;display:grid;place-items:center;flex:none;margin:-2px -4px 0 0}.cad-toast__close:hover{color:var(--cad-fg);background:var(--cad-hover)}.cad-toast__progress{position:absolute;left:0;bottom:0;height:2px;width:100%;background:var(--_c);opacity:.5;transform-origin:left;animation:cad-toast-life linear forwards}@keyframes cad-toast-in{0%{opacity:0;transform:translate(24px)}to{opacity:1;transform:none}}@keyframes cad-toast-in-l{0%{opacity:0;transform:translate(-24px)}to{opacity:1;transform:none}}@keyframes cad-toast-out{to{opacity:0;transform:scale(.96)}}@keyframes cad-toast-life{0%{transform:scaleX(1)}to{transform:scaleX(0)}}@media(prefers-reduced-motion:reduce){.cad-toast__item,.cad-toast__item.is-out{animation-duration:1ms}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }, { kind: "component", type: CadButtonComponent, selector: "button[cadButton], a[cadButton]", inputs: ["severity", "variant", "size", "loading", "disabled", "icon", "block", "value", "pressed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
12278
12380
  }
12279
12381
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadToastComponent, decorators: [{
12280
12382
  type: Component,
@@ -12307,7 +12409,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
12307
12409
  }
12308
12410
  </div>
12309
12411
  }
12310
- `, styles: [":host{position:fixed;display:flex;flex-direction:column;gap:8px;width:340px;max-width:calc(100vw - 32px);pointer-events:none;font-family:var(--cad-font)}:host(.pos-top-right){top:16px;right:16px}:host(.pos-top-left){top:16px;left:16px}:host(.pos-bottom-right){bottom:16px;right:16px;flex-direction:column-reverse}:host(.pos-bottom-left){bottom:16px;left:16px;flex-direction:column-reverse}:host(.pos-top-center){top:16px;left:50%;transform:translate(-50%)}:host(.pos-bottom-center){bottom:16px;left:50%;transform:translate(-50%);flex-direction:column-reverse}.cad-toast__item{--_c: var(--cad-accent);position:relative;overflow:hidden;pointer-events:auto;display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border-radius:10px;background:var(--cad-overlay);border:1px solid var(--cad-border);border-left:3px solid var(--_c);box-shadow:var(--cad-shadow-lg);color:var(--cad-fg);animation:cad-toast-in .18s var(--cad-ease)}.cad-toast__item.is-out{animation:cad-toast-out .16s var(--cad-ease) forwards}:host(.pos-top-left) .cad-toast__item,:host(.pos-bottom-left) .cad-toast__item{animation-name:cad-toast-in-l}.cad-toast__item.sev-success{--_c: var(--cad-success)}.cad-toast__item.sev-info{--_c: var(--cad-info)}.cad-toast__item.sev-warning{--_c: var(--cad-warning)}.cad-toast__item.sev-danger{--_c: var(--cad-danger)}.cad-toast__item.sev-secondary{--_c: var(--cad-fg-muted)}.cad-toast__item.sev-contrast{--_c: var(--cad-fg);background:var(--cad-fg);color:var(--cad-bg)}.cad-toast__icon{color:var(--_c);margin-top:1px;flex:none}.cad-toast__text{display:grid;gap:2px;flex:1;min-width:0}.cad-toast__text b{font-weight:600;font-size:13px}.cad-toast__text span{color:var(--cad-fg-secondary);font-size:12.5px;line-height:1.4}.sev-contrast .cad-toast__text span{color:inherit;opacity:.8}.cad-toast__actions{display:flex;gap:4px;margin-top:4px;margin-left:-8px}.cad-toast__close{border:0;background:transparent;color:var(--cad-fg-muted);cursor:pointer;padding:2px;border-radius:4px;display:grid;place-items:center;flex:none;margin:-2px -4px 0 0}.cad-toast__close:hover{color:var(--cad-fg);background:var(--cad-hover)}.cad-toast__progress{position:absolute;left:0;bottom:0;height:2px;width:100%;background:var(--_c);opacity:.5;transform-origin:left;animation:cad-toast-life linear forwards}@keyframes cad-toast-in{0%{opacity:0;transform:translate(24px)}to{opacity:1;transform:none}}@keyframes cad-toast-in-l{0%{opacity:0;transform:translate(-24px)}to{opacity:1;transform:none}}@keyframes cad-toast-out{to{opacity:0;transform:scale(.96)}}@keyframes cad-toast-life{0%{transform:scaleX(1)}to{transform:scaleX(0)}}@media(prefers-reduced-motion:reduce){.cad-toast__item,.cad-toast__item.is-out{animation-duration:1ms}}\n"] }]
12412
+ `, styles: [":host{position:fixed;display:flex;flex-direction:column;gap:8px;width:340px;max-width:calc(100% - 32px);pointer-events:none;font-family:var(--cad-font)}:host(.pos-top-right){top:16px;right:16px}:host(.pos-top-left){top:16px;left:16px}:host(.pos-bottom-right){bottom:16px;right:16px;flex-direction:column-reverse}:host(.pos-bottom-left){bottom:16px;left:16px;flex-direction:column-reverse}:host(.pos-top-center){top:16px;left:50%;transform:translate(-50%)}:host(.pos-bottom-center){bottom:16px;left:50%;transform:translate(-50%);flex-direction:column-reverse}.cad-toast__item{--_c: var(--cad-accent);position:relative;overflow:hidden;pointer-events:auto;display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border-radius:10px;background:var(--cad-overlay);border:1px solid var(--cad-border);border-left:3px solid var(--_c);box-shadow:var(--cad-shadow-lg);color:var(--cad-fg);animation:cad-toast-in .18s var(--cad-ease)}.cad-toast__item.is-out{animation:cad-toast-out .16s var(--cad-ease) forwards}:host(.pos-top-left) .cad-toast__item,:host(.pos-bottom-left) .cad-toast__item{animation-name:cad-toast-in-l}.cad-toast__item.sev-success{--_c: var(--cad-success)}.cad-toast__item.sev-info{--_c: var(--cad-info)}.cad-toast__item.sev-warning{--_c: var(--cad-warning)}.cad-toast__item.sev-danger{--_c: var(--cad-danger)}.cad-toast__item.sev-secondary{--_c: var(--cad-fg-muted)}.cad-toast__item.sev-contrast{--_c: var(--cad-fg);background:var(--cad-fg);color:var(--cad-bg)}.cad-toast__icon{color:var(--_c);margin-top:1px;flex:none}.cad-toast__text{display:grid;gap:2px;flex:1;min-width:0}.cad-toast__text b{font-weight:600;font-size:13px}.cad-toast__text span{color:var(--cad-fg-secondary);font-size:12.5px;line-height:1.4}.sev-contrast .cad-toast__text span{color:inherit;opacity:.8}.cad-toast__actions{display:flex;gap:4px;margin-top:4px;margin-left:-8px}.cad-toast__close{border:0;background:transparent;color:var(--cad-fg-muted);cursor:pointer;padding:2px;border-radius:4px;display:grid;place-items:center;flex:none;margin:-2px -4px 0 0}.cad-toast__close:hover{color:var(--cad-fg);background:var(--cad-hover)}.cad-toast__progress{position:absolute;left:0;bottom:0;height:2px;width:100%;background:var(--_c);opacity:.5;transform-origin:left;animation:cad-toast-life linear forwards}@keyframes cad-toast-in{0%{opacity:0;transform:translate(24px)}to{opacity:1;transform:none}}@keyframes cad-toast-in-l{0%{opacity:0;transform:translate(-24px)}to{opacity:1;transform:none}}@keyframes cad-toast-out{to{opacity:0;transform:scale(.96)}}@keyframes cad-toast-life{0%{transform:scaleX(1)}to{transform:scaleX(0)}}@media(prefers-reduced-motion:reduce){.cad-toast__item,.cad-toast__item.is-out{animation-duration:1ms}}\n"] }]
12311
12413
  }], ctorParameters: () => [], propDecorators: { position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: false }] }], life: [{ type: i0.Input, args: [{ isSignal: true, alias: "life", required: false }] }], maxVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxVisible", required: false }] }], preventDuplicates: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventDuplicates", required: false }] }], showProgress: [{ type: i0.Input, args: [{ isSignal: true, alias: "showProgress", required: false }] }], zIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "zIndex", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], clicked: [{ type: i0.Output, args: ["clicked"] }], itemTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => CadToastItemDirective), { isSignal: true }] }] } });
12312
12414
 
12313
12415
  /**
@@ -14783,7 +14885,7 @@ class CadMapSearchComponent {
14783
14885
  } @else if (open() && q().length >= 3 && !busy() && searched()) {
14784
14886
  <ul class="list"><li class="empty">Aucun résultat</li></ul>
14785
14887
  }
14786
- `, isInline: true, styles: [":host{display:block;position:relative;width:300px;max-width:calc(100vw - 80px);font:var(--cad-font-size) var(--cad-font);z-index:6}.box{display:flex;align-items:center;gap:8px;height:34px;padding:0 10px;background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:8px;box-shadow:0 2px 6px #0000001f;color:var(--cad-fg-muted);transition:border-color var(--cad-dur),box-shadow var(--cad-dur)}.box.has-focus{border-color:var(--cad-accent);box-shadow:0 0 0 3px var(--cad-ring)}input{flex:1 1 0;width:0;min-width:0;border:0;outline:0;background:transparent;color:var(--cad-fg);font:inherit}input::placeholder{color:var(--cad-fg-muted)}input::-webkit-search-cancel-button{display:none}.clr{border:0;background:transparent;color:var(--cad-fg-muted);cursor:pointer;display:grid;place-items:center;width:20px;height:20px;border-radius:4px;padding:0}.clr:hover{background:var(--cad-hover);color:var(--cad-fg)}.spin{color:var(--cad-accent)}.list{position:absolute;left:0;right:0;top:calc(100% + 6px);margin:0;padding:4px;list-style:none;background:var(--cad-overlay);border:1px solid var(--cad-border);border-radius:9px;box-shadow:var(--cad-shadow-lg);max-height:280px;overflow:auto}li{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:6px;cursor:pointer;color:var(--cad-fg);font-size:12.5px}li .lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}li small{color:var(--cad-fg-muted);font-size:11px;white-space:nowrap}li cad-icon{color:var(--cad-fg-muted)}li.is-active{background:var(--cad-hover)}li.empty{color:var(--cad-fg-muted);cursor:default;font-style:italic}\n"], dependencies: [{ kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14888
+ `, isInline: true, styles: [":host{display:block;position:relative;width:300px;max-width:calc(100% - 80px);font:var(--cad-font-size) var(--cad-font);z-index:6}.box{display:flex;align-items:center;gap:8px;height:34px;padding:0 10px;background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:8px;box-shadow:0 2px 6px #0000001f;color:var(--cad-fg-muted);transition:border-color var(--cad-dur),box-shadow var(--cad-dur)}.box.has-focus{border-color:var(--cad-accent);box-shadow:0 0 0 3px var(--cad-ring)}input{flex:1 1 0;width:0;min-width:0;border:0;outline:0;background:transparent;color:var(--cad-fg);font:inherit}input::placeholder{color:var(--cad-fg-muted)}input::-webkit-search-cancel-button{display:none}.clr{border:0;background:transparent;color:var(--cad-fg-muted);cursor:pointer;display:grid;place-items:center;width:20px;height:20px;border-radius:4px;padding:0}.clr:hover{background:var(--cad-hover);color:var(--cad-fg)}.spin{color:var(--cad-accent)}.list{position:absolute;left:0;right:0;top:calc(100% + 6px);margin:0;padding:4px;list-style:none;background:var(--cad-overlay);border:1px solid var(--cad-border);border-radius:9px;box-shadow:var(--cad-shadow-lg);max-height:280px;overflow:auto}li{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:6px;cursor:pointer;color:var(--cad-fg);font-size:12.5px}li .lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}li small{color:var(--cad-fg-muted);font-size:11px;white-space:nowrap}li cad-icon{color:var(--cad-fg-muted)}li.is-active{background:var(--cad-hover)}li.empty{color:var(--cad-fg-muted);cursor:default;font-style:italic}\n"], dependencies: [{ kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14787
14889
  }
14788
14890
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadMapSearchComponent, decorators: [{
14789
14891
  type: Component,
@@ -14806,7 +14908,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
14806
14908
  } @else if (open() && q().length >= 3 && !busy() && searched()) {
14807
14909
  <ul class="list"><li class="empty">Aucun résultat</li></ul>
14808
14910
  }
14809
- `, styles: [":host{display:block;position:relative;width:300px;max-width:calc(100vw - 80px);font:var(--cad-font-size) var(--cad-font);z-index:6}.box{display:flex;align-items:center;gap:8px;height:34px;padding:0 10px;background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:8px;box-shadow:0 2px 6px #0000001f;color:var(--cad-fg-muted);transition:border-color var(--cad-dur),box-shadow var(--cad-dur)}.box.has-focus{border-color:var(--cad-accent);box-shadow:0 0 0 3px var(--cad-ring)}input{flex:1 1 0;width:0;min-width:0;border:0;outline:0;background:transparent;color:var(--cad-fg);font:inherit}input::placeholder{color:var(--cad-fg-muted)}input::-webkit-search-cancel-button{display:none}.clr{border:0;background:transparent;color:var(--cad-fg-muted);cursor:pointer;display:grid;place-items:center;width:20px;height:20px;border-radius:4px;padding:0}.clr:hover{background:var(--cad-hover);color:var(--cad-fg)}.spin{color:var(--cad-accent)}.list{position:absolute;left:0;right:0;top:calc(100% + 6px);margin:0;padding:4px;list-style:none;background:var(--cad-overlay);border:1px solid var(--cad-border);border-radius:9px;box-shadow:var(--cad-shadow-lg);max-height:280px;overflow:auto}li{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:6px;cursor:pointer;color:var(--cad-fg);font-size:12.5px}li .lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}li small{color:var(--cad-fg-muted);font-size:11px;white-space:nowrap}li cad-icon{color:var(--cad-fg-muted)}li.is-active{background:var(--cad-hover)}li.empty{color:var(--cad-fg-muted);cursor:default;font-style:italic}\n"] }]
14911
+ `, styles: [":host{display:block;position:relative;width:300px;max-width:calc(100% - 80px);font:var(--cad-font-size) var(--cad-font);z-index:6}.box{display:flex;align-items:center;gap:8px;height:34px;padding:0 10px;background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:8px;box-shadow:0 2px 6px #0000001f;color:var(--cad-fg-muted);transition:border-color var(--cad-dur),box-shadow var(--cad-dur)}.box.has-focus{border-color:var(--cad-accent);box-shadow:0 0 0 3px var(--cad-ring)}input{flex:1 1 0;width:0;min-width:0;border:0;outline:0;background:transparent;color:var(--cad-fg);font:inherit}input::placeholder{color:var(--cad-fg-muted)}input::-webkit-search-cancel-button{display:none}.clr{border:0;background:transparent;color:var(--cad-fg-muted);cursor:pointer;display:grid;place-items:center;width:20px;height:20px;border-radius:4px;padding:0}.clr:hover{background:var(--cad-hover);color:var(--cad-fg)}.spin{color:var(--cad-accent)}.list{position:absolute;left:0;right:0;top:calc(100% + 6px);margin:0;padding:4px;list-style:none;background:var(--cad-overlay);border:1px solid var(--cad-border);border-radius:9px;box-shadow:var(--cad-shadow-lg);max-height:280px;overflow:auto}li{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:6px;cursor:pointer;color:var(--cad-fg);font-size:12.5px}li .lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}li small{color:var(--cad-fg-muted);font-size:11px;white-space:nowrap}li cad-icon{color:var(--cad-fg-muted)}li.is-active{background:var(--cad-hover)}li.empty{color:var(--cad-fg-muted);cursor:default;font-style:italic}\n"] }]
14810
14912
  }], ctorParameters: () => [], propDecorators: { inp: [{ type: i0.ViewChild, args: ['inp', { isSignal: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], limit: [{ type: i0.Input, args: [{ isSignal: true, alias: "limit", required: false }] }], debounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounce", required: false }] }], provider: [{ type: i0.Input, args: [{ isSignal: true, alias: "provider", required: false }] }], flyZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "flyZoom", required: false }] }], marker: [{ type: i0.Input, args: [{ isSignal: true, alias: "marker", required: false }] }], nearMap: [{ type: i0.Input, args: [{ isSignal: true, alias: "nearMap", required: false }] }], select: [{ type: i0.Output, args: ["select"] }], cleared: [{ type: i0.Output, args: ["cleared"] }] } });
14811
14913
  const escapeHtml = (s) => s.replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]);
14812
14914
 
@@ -17994,9 +18096,1453 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
17994
18096
  `, styles: [":host{display:block;font-family:var(--cad-font)}.cad-chart__box{position:relative;width:100%;height:var(--cad-chart-h, 280px)}.cad-chart__box canvas{width:100%!important;height:100%!important}.cad-chart__state{position:absolute;inset:0;display:grid;place-items:center}.cad-chart__table{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}:host(.show-table) .cad-chart__table{position:static;width:100%;height:auto;clip:auto;white-space:normal;margin-top:8px;border-collapse:collapse;font-size:12px}:host(.show-table) .cad-chart__table th,:host(.show-table) .cad-chart__table td{border:1px solid var(--cad-border);padding:4px 8px;text-align:right}:host(.show-table) .cad-chart__table th{background:var(--cad-bg-subtle);text-align:left}\n"] }]
17995
18097
  }], ctorParameters: () => [], propDecorators: { canvas: [{ type: i0.ViewChild, args: ['canvas', { isSignal: true }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], palette: [{ type: i0.Input, args: [{ isSignal: true, alias: "palette", required: false }] }], themed: [{ type: i0.Input, args: [{ isSignal: true, alias: "themed", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], accessibleTable: [{ type: i0.Input, args: [{ isSignal: true, alias: "accessibleTable", required: false }] }], chartClick: [{ type: i0.Output, args: ["chartClick"] }], chartHover: [{ type: i0.Output, args: ["chartHover"] }], ready: [{ type: i0.Output, args: ["ready"] }], error: [{ type: i0.Output, args: ["error"] }] } });
17996
18098
 
18099
+ /**
18100
+ * Modèle de planification commun aux trois vues (taskboard, scheduler, gantt).
18101
+ *
18102
+ * DOCTRINE — aucun champ n'est obligatoire hors `id`. Chaque vue déclare ses prérequis et
18103
+ * son repli : une tâche qu'une vue ne sait pas placer part dans sa zone « non planifié »,
18104
+ * jamais dans le silence. C'est cette règle, et elle seule, qui rend le document commun.
18105
+ *
18106
+ * Les noms suivent les conventions EXISTANTES plutôt qu'une invention maison :
18107
+ * - RFC 5545 (iCalendar) pour l'événement daté et sa récurrence : rrule, exdates, recurrenceId ;
18108
+ * - MS Project / Primavera P6 pour les liens FS/SS/FF/SF et le décalage (lag).
18109
+ * camelCase partout : c'est la convention du reste de la bibliothèque ET celle de slapi,
18110
+ * ce qui donne une correspondance 1 pour 1 entre le JSON et les colonnes.
18111
+ */
18112
+
18113
+ /**
18114
+ * Durées en unités OUVRÉES : '3d', '4h', '90m', '2w', '1.5d', '-2d'.
18115
+ *
18116
+ * Une durée n'a de sens qu'appuyée sur un calendrier : 'd' vaut une JOURNÉE OUVRÉE, dont la
18117
+ * longueur dépend des horaires déclarés (8 h ouvrées ou 24 h). La conversion en minutes se
18118
+ * fait donc via `CadWorkCalendar.dailyMinutes`, jamais par une constante.
18119
+ *
18120
+ * 'w' vaut une semaine OUVRÉE = nombre de jours ouvrés déclarés (5 par défaut), pas 7 jours.
18121
+ */
18122
+ const RE = /^\s*([+-]?\d+(?:[.,]\d+)?)\s*(m|min|h|d|j|w|s)\s*$/i;
18123
+ /**
18124
+ * Analyse une durée. Accepte les alias français ('j' = jour, 's' = semaine) et la virgule
18125
+ * décimale. Rend null si la forme est invalide — l'appelant en fait une anomalie, pas un plantage.
18126
+ */
18127
+ function cadParseDuration(input) {
18128
+ if (input == null || input === '')
18129
+ return null;
18130
+ if (typeof input === 'number')
18131
+ return Number.isFinite(input) ? { value: input, unit: 'd' } : null;
18132
+ const m = RE.exec(String(input));
18133
+ if (!m)
18134
+ return null;
18135
+ const value = Number(m[1].replace(',', '.'));
18136
+ if (!Number.isFinite(value))
18137
+ return null;
18138
+ const u = m[2].toLowerCase();
18139
+ const unit = u === 'min' ? 'm' : u === 'j' ? 'd' : u === 's' ? 'w' : u;
18140
+ return { value, unit };
18141
+ }
18142
+ /**
18143
+ * Convertit une durée en MINUTES OUVRÉES.
18144
+ * `dailyMinutes` = minutes ouvrées d'une journée type, `weekDays` = jours ouvrés par semaine.
18145
+ */
18146
+ function cadDurationToMinutes(d, dailyMinutes, weekDays) {
18147
+ if (!d)
18148
+ return 0;
18149
+ switch (d.unit) {
18150
+ case 'm': return d.value;
18151
+ case 'h': return d.value * 60;
18152
+ case 'd': return d.value * dailyMinutes;
18153
+ case 'w': return d.value * dailyMinutes * Math.max(1, weekDays);
18154
+ }
18155
+ }
18156
+ /** Rend une durée sous sa forme la plus lisible ('3d', '4h', '90m') depuis des minutes ouvrées. */
18157
+ function cadFormatDuration(minutes, dailyMinutes) {
18158
+ const abs = Math.abs(minutes);
18159
+ const sign = minutes < 0 ? '-' : '';
18160
+ if (dailyMinutes > 0 && abs % dailyMinutes === 0)
18161
+ return sign + abs / dailyMinutes + 'd';
18162
+ if (abs % 60 === 0)
18163
+ return sign + abs / 60 + 'h';
18164
+ return sign + abs + 'm';
18165
+ }
18166
+
18167
+ /**
18168
+ * Rang FRACTIONNAIRE : une chaîne ordonnable dans laquelle on peut toujours insérer.
18169
+ *
18170
+ * POURQUOI — déplacer une carte entre deux autres ne doit pas réécrire toute la colonne en
18171
+ * base. À plusieurs utilisateurs, une renumérotation entière garantit les collisions et coûte
18172
+ * un UPDATE par carte. Ici, insérer entre deux voisins produit une nouvelle chaîne comprise
18173
+ * entre les leurs : UN SEUL enregistrement change.
18174
+ *
18175
+ * C'est un FORMAT STOCKÉ, pas une astuce d'affichage — d'où sa place au socle, avant toute vue.
18176
+ *
18177
+ * INVARIANT — un rang produit ici ne se termine JAMAIS par le caractère plancher ('0'), sans
18178
+ * quoi il existerait des couples sans intervalle (rien ne tient entre '0' et '00'). Tous les
18179
+ * rangs manipulés doivent donc venir de ces fonctions.
18180
+ */
18181
+ const A = '0123456789abcdefghijklmnopqrstuvwxyz';
18182
+ const RADIX = A.length;
18183
+ const CH = (v) => A[Math.max(0, Math.min(RADIX - 1, v))];
18184
+ const IDX = (c) => {
18185
+ const i = A.indexOf(c);
18186
+ return i < 0 ? 0 : i;
18187
+ };
18188
+ /** Vrai si la chaîne est un rang exploitable (alphabet connu, pas de plancher final). */
18189
+ function cadIsRank(s) {
18190
+ return typeof s === 'string' && s.length > 0 && !s.endsWith(A[0]) && [...s].every(c => A.includes(c));
18191
+ }
18192
+ /**
18193
+ * Rang strictement compris entre `prev` et `next`.
18194
+ * `prev` absent = insertion en tête, `next` absent = insertion en queue, les deux = première carte.
18195
+ */
18196
+ function cadRankBetween(prev, next) {
18197
+ const a = cadIsRank(prev) ? prev : '';
18198
+ const b = cadIsRank(next) ? next : '';
18199
+ if (a && b && a >= b)
18200
+ throw new Error('cadRankBetween : ' + a + ' n\'est pas avant ' + b);
18201
+ let out = '';
18202
+ for (let i = 0; i < 64; i++) {
18203
+ const va = i < a.length ? IDX(a[i]) : 0; // a épuisé -> plancher
18204
+ const vb = i < b.length ? IDX(b[i]) : RADIX; // b épuisé -> plafond ouvert
18205
+ if (va + 1 < vb)
18206
+ return out + CH((va + vb) >> 1); // de la place : on coupe au milieu
18207
+ out += i < a.length ? a[i] : A[0]; // pas de place : on descend d'un cran
18208
+ }
18209
+ /* Inatteignable en pratique : 64 caractères = plus d'insertions successives au même point
18210
+ que ce qu'une session humaine produit. Le repli reste ordonné. */
18211
+ return out + CH(RADIX >> 1);
18212
+ }
18213
+ /** Rang d'une carte posée AVANT toutes les autres. */
18214
+ const cadRankFirst = (first) => cadRankBetween(null, first);
18215
+ /** Rang d'une carte posée APRÈS toutes les autres. */
18216
+ const cadRankLast = (last) => cadRankBetween(last, null);
18217
+ /**
18218
+ * `n` rangs croissants bien répartis — pour initialiser une colonne ou rattraper des données
18219
+ * qui n'en portaient pas. La répartition laisse de la place entre chaque voisin.
18220
+ */
18221
+ function cadRankSequence(n) {
18222
+ const count = Math.max(0, Math.floor(n));
18223
+ if (!count)
18224
+ return [];
18225
+ const span = RADIX * RADIX;
18226
+ const out = [];
18227
+ for (let i = 0; i < count; i++) {
18228
+ let v = Math.floor(((i + 1) * span) / (count + 1));
18229
+ if (v % RADIX === 0)
18230
+ v += 1; // jamais de plancher final
18231
+ out.push(CH(Math.floor(v / RADIX)) + CH(v % RADIX));
18232
+ }
18233
+ return out;
18234
+ }
18235
+ /** Comparateur de rangs, utilisable tel quel dans un `sort`. */
18236
+ const cadCompareRank = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
18237
+
18238
+ /**
18239
+ * Calendrier ouvré — le référentiel temps partagé par le scheduler et le gantt.
18240
+ *
18241
+ * Tout se calcule en HEURE MURALE locale (pas en millisecondes écoulées) : « 3 jours ouvrés »
18242
+ * veut dire trois fois les horaires déclarés, que la période traverse ou non un changement
18243
+ * d'heure. C'est la seule sémantique qui corresponde à ce qu'un planificateur attend.
18244
+ *
18245
+ * Les exceptions peuvent viser une ressource précise : un congé n'est pas un jour férié.
18246
+ * La recherche est donc toujours « exception de CETTE ressource, sinon exception globale ».
18247
+ */
18248
+ /** Au-delà, on considère le calendrier impraticable plutôt que de boucler (10 ans de jours). */
18249
+ const MAX_SCAN = 3660;
18250
+ const DAY = 1440;
18251
+ const dayStart = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
18252
+ const minutesOf = (d) => d.getHours() * 60 + d.getMinutes();
18253
+ const atMinutes = (day, min) => new Date(day.getFullYear(), day.getMonth(), day.getDate(), Math.floor(min / 60), Math.round(min % 60));
18254
+ /** 'HH:mm' -> minutes depuis minuit. '24:00' vaut la fin de journée. NaN si illisible. */
18255
+ function parseClock(v) {
18256
+ const m = /^\s*(\d{1,2}):(\d{2})\s*$/.exec(String(v ?? ''));
18257
+ if (!m)
18258
+ return NaN;
18259
+ const min = +m[1] * 60 + +m[2];
18260
+ return min >= 0 && min <= DAY ? min : NaN;
18261
+ }
18262
+ /** Nettoie une liste d'horaires : bornes valides, tri, fusion des recouvrements. */
18263
+ function normalizeHours(hours) {
18264
+ const raw = [];
18265
+ for (const h of hours ?? []) {
18266
+ const a = parseClock(h?.from);
18267
+ const b = parseClock(h?.to);
18268
+ if (Number.isNaN(a) || Number.isNaN(b) || b <= a)
18269
+ continue;
18270
+ raw.push([a, b]);
18271
+ }
18272
+ raw.sort((x, y) => x[0] - y[0]);
18273
+ const out = [];
18274
+ for (const [a, b] of raw) {
18275
+ const last = out[out.length - 1];
18276
+ if (last && a <= last[1])
18277
+ last[1] = Math.max(last[1], b);
18278
+ else
18279
+ out.push([a, b]);
18280
+ }
18281
+ return out;
18282
+ }
18283
+ const FULL_DAY = [[0, DAY]];
18284
+ /**
18285
+ * Jours fériés français d'une année, au format 'yyyy-MM-dd' — les huit dates fixes plus les
18286
+ * trois mobiles adossées à Pâques. Évite d'avoir à saisir la liste à la main chaque année.
18287
+ */
18288
+ function cadFrenchHolidays(year) {
18289
+ // Pâques, algorithme grégorien anonyme.
18290
+ const a = year % 19;
18291
+ const b = Math.floor(year / 100);
18292
+ const c = year % 100;
18293
+ const d = Math.floor(b / 4);
18294
+ const e = b % 4;
18295
+ const f = Math.floor((b + 8) / 25);
18296
+ const g = Math.floor((b - f + 1) / 3);
18297
+ const h = (19 * a + b - d - g + 15) % 30;
18298
+ const i = Math.floor(c / 4);
18299
+ const k = c % 4;
18300
+ const l = (32 + 2 * e + 2 * i - h - k) % 7;
18301
+ const m = Math.floor((a + 11 * h + 22 * l) / 451);
18302
+ const month = Math.floor((h + l - 7 * m + 114) / 31);
18303
+ const day = ((h + l - 7 * m + 114) % 31) + 1;
18304
+ const easter = new Date(year, month - 1, day);
18305
+ const fixed = ['01-01', '05-01', '05-08', '07-14', '08-15', '11-01', '11-11', '12-25'];
18306
+ const mobile = [1, 39, 50].map(n => isoOf(addDays(easter, n)));
18307
+ return [...fixed.map(s => year + '-' + s), ...mobile].sort();
18308
+ }
18309
+ /**
18310
+ * Calendrier ouvré immuable. Toute la logique est pure : elle se teste sans DOM et sans horloge.
18311
+ *
18312
+ * ```ts
18313
+ * const cal = new CadWorkCalendar({ workdays: [1,2,3,4,5], holidays: cadFrenchHolidays(2026) });
18314
+ * cal.addMinutes(new Date(2026, 7, 21, 16, 0), 4 * 60); // vendredi 16 h + 4 h ouvrées -> lundi
18315
+ * ```
18316
+ */
18317
+ class CadWorkCalendar {
18318
+ /** Jours ouvrés, 0 = dimanche. */
18319
+ workdays;
18320
+ /** Minutes ouvrées d'une journée type — sert d'étalon à l'unité 'd'. */
18321
+ dailyMinutes;
18322
+ /** Jours ouvrés par semaine — sert d'étalon à l'unité 'w'. */
18323
+ weekDays;
18324
+ base;
18325
+ holidays;
18326
+ /** Clé 'yyyy-MM-dd' pour une exception globale, 'yyyy-MM-dd|ressource' pour une exception ciblée. */
18327
+ exceptions;
18328
+ constructor(spec) {
18329
+ /* Un défaut ne s'applique qu'à ce qui n'a PAS été déclaré. Déclarer `workdays: []`, c'est
18330
+ dire que rien n'est ouvert : le remplacer par lundi-vendredi reviendrait à contredire
18331
+ l'appelant en silence, et rendrait `empty` inatteignable. */
18332
+ this.workdays = new Set(spec?.workdays === undefined
18333
+ ? [1, 2, 3, 4, 5]
18334
+ : spec.workdays.filter(n => Number.isInteger(n) && n >= 0 && n <= 6));
18335
+ this.base = spec?.hours === undefined ? FULL_DAY : normalizeHours(spec.hours);
18336
+ this.dailyMinutes = this.base.reduce((s, [a, b]) => s + (b - a), 0);
18337
+ this.weekDays = this.workdays.size;
18338
+ this.holidays = new Set((spec?.holidays ?? []).filter(s => typeof s === 'string'));
18339
+ const ex = new Map();
18340
+ for (const e of spec?.exceptions ?? []) {
18341
+ if (!e?.date)
18342
+ continue;
18343
+ const key = e.resource ? e.date + '|' + e.resource : e.date;
18344
+ ex.set(key, e.off ? null : normalizeHours(e.hours ?? []));
18345
+ }
18346
+ this.exceptions = ex;
18347
+ }
18348
+ /** Vrai si aucun temps ouvré n'est déclarable — le normaliseur en fait une anomalie. */
18349
+ get empty() {
18350
+ return this.workdays.size === 0 || this.dailyMinutes === 0;
18351
+ }
18352
+ /** Intervalles ouvrés d'une journée, en minutes depuis minuit. Vide = jour chômé. */
18353
+ intervals(date, resource) {
18354
+ if (!isValidDate(date))
18355
+ return [];
18356
+ const iso = isoOf(date);
18357
+ if (resource) {
18358
+ const scoped = this.exceptions.get(iso + '|' + resource);
18359
+ if (scoped !== undefined)
18360
+ return scoped ?? [];
18361
+ // une exception ciblée peut aussi OUVRIR un jour normalement chômé
18362
+ }
18363
+ const global = this.exceptions.get(iso);
18364
+ if (global !== undefined)
18365
+ return global ?? [];
18366
+ if (this.holidays.has(iso))
18367
+ return [];
18368
+ return this.workdays.has(date.getDay()) ? this.base : [];
18369
+ }
18370
+ /** Vrai si la journée comporte au moins un intervalle ouvré. */
18371
+ isWorkday(date, resource) {
18372
+ return this.intervals(date, resource).length > 0;
18373
+ }
18374
+ /** Vrai si l'instant tombe DANS un intervalle ouvré (borne de fin exclue). */
18375
+ isWorkingTime(date, resource) {
18376
+ if (!isValidDate(date))
18377
+ return false;
18378
+ const m = minutesOf(date);
18379
+ return this.intervals(date, resource).some(([a, b]) => m >= a && m < b);
18380
+ }
18381
+ /** Premier instant ouvré à partir de `date` (`date` lui-même s'il l'est). null si introuvable. */
18382
+ nextWorkingStart(date, resource) {
18383
+ const c = this.scanForward(this.cursorOf(date), resource);
18384
+ return c ? atMinutes(c.day, c.min) : null;
18385
+ }
18386
+ /** Dernier instant ouvré jusqu'à `date` (exclu). null si introuvable. */
18387
+ prevWorkingEnd(date, resource) {
18388
+ const c = this.scanBackward(this.cursorOf(date), resource);
18389
+ return c ? atMinutes(c.day, c.min) : null;
18390
+ }
18391
+ /**
18392
+ * Avance (ou recule, si négatif) de `minutes` OUVRÉES depuis `from`.
18393
+ * Rend null si le calendrier ne permet pas d'y arriver (aucun jour ouvré déclaré).
18394
+ */
18395
+ addMinutes(from, minutes, resource) {
18396
+ if (!isValidDate(from) || !Number.isFinite(minutes))
18397
+ return null;
18398
+ if (minutes === 0)
18399
+ return this.nextWorkingStart(from, resource);
18400
+ return minutes > 0
18401
+ ? this.walkForward(from, minutes, resource)
18402
+ : this.walkBackward(from, -minutes, resource);
18403
+ }
18404
+ /** Minutes OUVRÉES entre deux instants. Négatif si `b` précède `a`. */
18405
+ minutesBetween(a, b, resource) {
18406
+ if (!isValidDate(a) || !isValidDate(b))
18407
+ return 0;
18408
+ if (b.getTime() < a.getTime())
18409
+ return -this.minutesBetween(b, a, resource);
18410
+ const end = this.cursorOf(b);
18411
+ let day = dayStart(a);
18412
+ let from = minutesOf(a);
18413
+ let total = 0;
18414
+ for (let guard = 0; guard < MAX_SCAN; guard++) {
18415
+ const last = day.getTime() >= end.day.getTime();
18416
+ const to = last ? end.min : DAY;
18417
+ for (const [s, e] of this.intervals(day, resource)) {
18418
+ const lo = Math.max(s, from);
18419
+ const hi = Math.min(e, to);
18420
+ if (hi > lo)
18421
+ total += hi - lo;
18422
+ }
18423
+ if (last)
18424
+ return total;
18425
+ day = addDays(day, 1);
18426
+ from = 0;
18427
+ }
18428
+ return total;
18429
+ }
18430
+ /* ---------------- interne : un curseur (jour, minute) évite l'ambiguïté de 24:00 -------- */
18431
+ cursorOf(d) {
18432
+ return { day: dayStart(d), min: minutesOf(d) };
18433
+ }
18434
+ scanForward(c, resource) {
18435
+ if (this.empty)
18436
+ return null;
18437
+ let { day, min } = c;
18438
+ for (let i = 0; i < MAX_SCAN; i++) {
18439
+ for (const [s, e] of this.intervals(day, resource)) {
18440
+ if (e > min)
18441
+ return { day, min: Math.max(s, min) };
18442
+ }
18443
+ day = addDays(day, 1);
18444
+ min = 0;
18445
+ }
18446
+ return null;
18447
+ }
18448
+ scanBackward(c, resource) {
18449
+ if (this.empty)
18450
+ return null;
18451
+ let { day, min } = c;
18452
+ for (let i = 0; i < MAX_SCAN; i++) {
18453
+ const ivs = this.intervals(day, resource);
18454
+ for (let k = ivs.length - 1; k >= 0; k--) {
18455
+ const [s, e] = ivs[k];
18456
+ if (s < min)
18457
+ return { day, min: Math.min(e, min) };
18458
+ }
18459
+ day = addDays(day, -1);
18460
+ min = DAY;
18461
+ }
18462
+ return null;
18463
+ }
18464
+ walkForward(from, minutes, resource) {
18465
+ let c = this.scanForward(this.cursorOf(from), resource);
18466
+ if (!c)
18467
+ return null;
18468
+ let left = minutes;
18469
+ for (let guard = 0; guard < MAX_SCAN; guard++) {
18470
+ for (const [s, e] of this.intervals(c.day, resource)) {
18471
+ if (e <= c.min)
18472
+ continue;
18473
+ const start = Math.max(s, c.min);
18474
+ const avail = e - start;
18475
+ if (avail >= left)
18476
+ return atMinutes(c.day, start + left);
18477
+ left -= avail;
18478
+ }
18479
+ c = { day: addDays(c.day, 1), min: 0 };
18480
+ }
18481
+ return null;
18482
+ }
18483
+ walkBackward(from, minutes, resource) {
18484
+ let c = this.scanBackward(this.cursorOf(from), resource);
18485
+ if (!c)
18486
+ return null;
18487
+ let left = minutes;
18488
+ for (let guard = 0; guard < MAX_SCAN; guard++) {
18489
+ const ivs = this.intervals(c.day, resource);
18490
+ for (let k = ivs.length - 1; k >= 0; k--) {
18491
+ const [s, e] = ivs[k];
18492
+ if (s >= c.min)
18493
+ continue;
18494
+ const end = Math.min(e, c.min);
18495
+ const avail = end - s;
18496
+ if (avail >= left)
18497
+ return atMinutes(c.day, end - left);
18498
+ left -= avail;
18499
+ }
18500
+ c = { day: addDays(c.day, -1), min: DAY };
18501
+ }
18502
+ return null;
18503
+ }
18504
+ }
18505
+
18506
+ /**
18507
+ * Axe de temps : graduations, et conversion pixel <-> date.
18508
+ *
18509
+ * LE POINT CRITIQUE — l'axe n'est PAS une division. Écrire `x = (t - t0) / msParPixel` est
18510
+ * faux dès qu'on dézoome : un jour ne fait pas toujours 24 h (changement d'heure), un mois n'a
18511
+ * pas de largeur fixe, une année pas davantage. Février et mars occuperaient alors des largeurs
18512
+ * différentes alors que l'oeil attend des colonnes égales, et le défaut ne se voit qu'en mars
18513
+ * et en octobre — donc jamais pendant le développement.
18514
+ *
18515
+ * Ici, l'axe est une LISTE DE GRADUATIONS de largeur égale, et la conversion est une recherche
18516
+ * dans cette liste suivie d'une interpolation À L'INTÉRIEUR de la graduation trouvée. Chaque
18517
+ * graduation porte sa vraie durée ; l'arithmétique linéaire ne s'applique que localement.
18518
+ *
18519
+ * Corollaire de rendu : la grille de fond n'a pas à exister en DOM. Des graduations de largeur
18520
+ * égale se peignent en `repeating-linear-gradient`, ce qui évite de virtualiser 1 100 colonnes
18521
+ * pour trois ans au jour. Seuls l'en-tête visible et les barres sont rendus.
18522
+ */
18523
+ /** Échelle du bandeau supérieur d'un en-tête à deux étages. */
18524
+ const PARENT = {
18525
+ hour: 'day',
18526
+ day: 'month',
18527
+ week: 'month',
18528
+ month: 'year',
18529
+ quarter: 'year',
18530
+ year: null,
18531
+ };
18532
+ const MAX_TICKS = 20000;
18533
+ const startOfWeek = (d, firstDay) => {
18534
+ const s = startOfDay(d);
18535
+ const delta = (s.getDay() - firstDay + 7) % 7;
18536
+ return addDays(s, -delta);
18537
+ };
18538
+ const startOfQuarter = (d) => new Date(d.getFullYear(), Math.floor(d.getMonth() / 3) * 3, 1);
18539
+ const startOfHour = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours());
18540
+ /** Aligne un instant sur le début de sa graduation. */
18541
+ function cadScaleStart(date, scale, firstDay = 1) {
18542
+ switch (scale) {
18543
+ case 'hour': return startOfHour(date);
18544
+ case 'day': return startOfDay(date);
18545
+ case 'week': return startOfWeek(date, firstDay);
18546
+ case 'month': return startOfMonth(date);
18547
+ case 'quarter': return startOfQuarter(date);
18548
+ case 'year': return new Date(date.getFullYear(), 0, 1);
18549
+ }
18550
+ }
18551
+ /**
18552
+ * Début de la graduation SUIVANTE. Construit en heure locale, donc juste aux changements d'heure.
18553
+ * L'entrée est alignée au préalable : `cadScaleNext(31 janvier, 'month')` rend le 1er février,
18554
+ * jamais le 28 — sans quoi une date de milieu de mois produirait une graduation décalée.
18555
+ */
18556
+ function cadScaleNext(date, scale, firstDay = 1) {
18557
+ const start = cadScaleStart(date, scale, firstDay);
18558
+ switch (scale) {
18559
+ case 'hour': return new Date(start.getFullYear(), start.getMonth(), start.getDate(), start.getHours() + 1);
18560
+ case 'day': return addDays(start, 1);
18561
+ case 'week': return addDays(start, 7);
18562
+ case 'month': return addMonths(start, 1);
18563
+ case 'quarter': return addMonths(start, 3);
18564
+ case 'year': return addYears(start, 1);
18565
+ }
18566
+ }
18567
+ const intl = (locale, opts) => new Intl.DateTimeFormat(locale, opts);
18568
+ function defaultLabel(d, scale, locale) {
18569
+ switch (scale) {
18570
+ case 'hour': return intl(locale, { hour: '2-digit', minute: '2-digit' }).format(d);
18571
+ case 'day': return String(d.getDate());
18572
+ case 'week': return 'S' + isoWeek(d);
18573
+ case 'month': return intl(locale, { month: 'short' }).format(d);
18574
+ case 'quarter': return 'T' + (Math.floor(d.getMonth() / 3) + 1);
18575
+ case 'year': return String(d.getFullYear());
18576
+ }
18577
+ }
18578
+ /** Libellé du bandeau supérieur — plus explicite, puisqu'il porte le contexte. */
18579
+ function bandLabel(d, scale, locale) {
18580
+ switch (scale) {
18581
+ case 'day': return intl(locale, { weekday: 'short', day: 'numeric', month: 'long' }).format(d);
18582
+ case 'month': return intl(locale, { month: 'long', year: 'numeric' }).format(d);
18583
+ case 'year': return String(d.getFullYear());
18584
+ default: return defaultLabel(d, scale, locale);
18585
+ }
18586
+ }
18587
+ /** Une graduation est « majeure » quand elle ouvre l'unité supérieure. */
18588
+ function isMajor(d, scale, firstDay) {
18589
+ switch (scale) {
18590
+ case 'hour': return d.getHours() === 0;
18591
+ case 'day': return d.getDate() === 1;
18592
+ case 'week': return d.getDate() <= 7;
18593
+ case 'month': return d.getMonth() === 0;
18594
+ case 'quarter': return d.getMonth() === 0;
18595
+ case 'year': return true;
18596
+ }
18597
+ }
18598
+ /**
18599
+ * Graduations couvrant [from, to], alignées sur l'échelle.
18600
+ * La dernière graduation dépasse `to` si nécessaire : un axe ne coupe jamais une unité en deux.
18601
+ */
18602
+ function cadPlanTicks(from, to, scale, options = {}) {
18603
+ if (!isValidDate(from) || !isValidDate(to) || to.getTime() < from.getTime())
18604
+ return [];
18605
+ const locale = options.locale ?? 'fr-FR';
18606
+ const firstDay = options.firstDay ?? 1;
18607
+ const label = options.label ?? defaultLabel;
18608
+ const cal = options.calendar ?? null;
18609
+ const today = options.today === null ? null : (options.today ?? new Date());
18610
+ const max = Math.max(1, options.max ?? MAX_TICKS);
18611
+ const limit = to.getTime();
18612
+ const now = today ? today.getTime() : NaN;
18613
+ const out = [];
18614
+ let start = cadScaleStart(from, scale, firstDay);
18615
+ for (let i = 0; i < max; i++) {
18616
+ const end = cadScaleNext(start, scale, firstDay);
18617
+ out.push({
18618
+ start,
18619
+ end,
18620
+ label: label(start, scale, locale),
18621
+ major: isMajor(start, scale, firstDay),
18622
+ off: cal
18623
+ ? scale === 'hour'
18624
+ ? !cal.isWorkingTime(start)
18625
+ : scale === 'day'
18626
+ ? !cal.isWorkday(start)
18627
+ : false
18628
+ : false,
18629
+ today: today ? now >= start.getTime() && now < end.getTime() : false,
18630
+ });
18631
+ if (end.getTime() > limit)
18632
+ break;
18633
+ start = end;
18634
+ }
18635
+ return out;
18636
+ }
18637
+ function cadPlanHeader(from, to, scale, options = {}) {
18638
+ const lower = cadPlanTicks(from, to, scale, options);
18639
+ const parent = PARENT[scale];
18640
+ if (!parent || !lower.length)
18641
+ return { upper: [], lower };
18642
+ const upper = cadPlanTicks(lower[0].start, lower[lower.length - 1].end, parent, {
18643
+ ...options,
18644
+ calendar: null,
18645
+ label: options.label ?? bandLabel,
18646
+ });
18647
+ return { upper, lower };
18648
+ }
18649
+ /**
18650
+ * Conversion pixel <-> date sur une liste de graduations de largeur égale.
18651
+ *
18652
+ * Hors de l'axe, la position est EXTRAPOLÉE sur la durée de la graduation de bord plutôt que
18653
+ * rabattue sur la borne : une barre qui commence avant la fenêtre garde une position cohérente,
18654
+ * et l'appelant décide seul de la découper ou de la masquer.
18655
+ */
18656
+ class CadTimeAxis {
18657
+ ticks;
18658
+ unitWidth;
18659
+ constructor(ticks,
18660
+ /** Largeur d'une graduation, en pixels. */
18661
+ unitWidth) {
18662
+ this.ticks = ticks;
18663
+ this.unitWidth = unitWidth;
18664
+ }
18665
+ get width() { return this.ticks.length * this.unitWidth; }
18666
+ get start() { return this.ticks.length ? this.ticks[0].start : null; }
18667
+ get end() { return this.ticks.length ? this.ticks[this.ticks.length - 1].end : null; }
18668
+ /** Indice de la graduation contenant `t` (millisecondes), par recherche binaire. */
18669
+ indexOf(t) {
18670
+ let lo = 0;
18671
+ let hi = this.ticks.length - 1;
18672
+ while (lo < hi) {
18673
+ const mid = (lo + hi + 1) >> 1;
18674
+ if (this.ticks[mid].start.getTime() <= t)
18675
+ lo = mid;
18676
+ else
18677
+ hi = mid - 1;
18678
+ }
18679
+ return lo;
18680
+ }
18681
+ /** Abscisse d'une date, en pixels depuis le début de l'axe. */
18682
+ xOf(date) {
18683
+ const n = this.ticks.length;
18684
+ if (!n || !isValidDate(date))
18685
+ return 0;
18686
+ const t = date.getTime();
18687
+ const first = this.ticks[0];
18688
+ const last = this.ticks[n - 1];
18689
+ if (t < first.start.getTime()) {
18690
+ const len = first.end.getTime() - first.start.getTime();
18691
+ return len ? ((t - first.start.getTime()) / len) * this.unitWidth : 0;
18692
+ }
18693
+ if (t >= last.end.getTime()) {
18694
+ const len = last.end.getTime() - last.start.getTime();
18695
+ return this.width + (len ? ((t - last.end.getTime()) / len) * this.unitWidth : 0);
18696
+ }
18697
+ const i = this.indexOf(t);
18698
+ const tk = this.ticks[i];
18699
+ const len = tk.end.getTime() - tk.start.getTime();
18700
+ return (i + (len ? (t - tk.start.getTime()) / len : 0)) * this.unitWidth;
18701
+ }
18702
+ /** Date à une abscisse donnée. */
18703
+ dateAt(x) {
18704
+ const n = this.ticks.length;
18705
+ if (!n || !Number.isFinite(x) || this.unitWidth <= 0)
18706
+ return null;
18707
+ const u = x / this.unitWidth;
18708
+ const i = Math.floor(u);
18709
+ if (i < 0) {
18710
+ const first = this.ticks[0];
18711
+ const len = first.end.getTime() - first.start.getTime();
18712
+ return new Date(first.start.getTime() + u * len);
18713
+ }
18714
+ if (i >= n) {
18715
+ const last = this.ticks[n - 1];
18716
+ const len = last.end.getTime() - last.start.getTime();
18717
+ return new Date(last.end.getTime() + (u - n) * len);
18718
+ }
18719
+ const tk = this.ticks[i];
18720
+ const len = tk.end.getTime() - tk.start.getTime();
18721
+ return new Date(tk.start.getTime() + (u - i) * len);
18722
+ }
18723
+ /** Position et largeur d'une barre. La largeur ne descend jamais sous zéro. */
18724
+ spanOf(start, end) {
18725
+ const x = this.xOf(start);
18726
+ return { x, width: Math.max(0, this.xOf(end) - x) };
18727
+ }
18728
+ }
18729
+
18730
+ /**
18731
+ * Normalisation du document de planification : ce que les trois vues consomment réellement.
18732
+ *
18733
+ * RÈGLE FONDATRICE — hors `id`, aucun champ n'est obligatoire. Une tâche sans dates reste une
18734
+ * tâche : le taskboard la montre, le gantt et le scheduler la rangent dans leur zone « non
18735
+ * planifié ». Rien n'est jamais écarté en silence ; tout écart devient une entrée de `issues`
18736
+ * portant un code stable, que l'appelant affiche ou ignore, mais qu'il peut toujours lire.
18737
+ *
18738
+ * Le normaliseur ne DÉCIDE de rien qu'il ne puisse justifier : il ne replanifie pas, ne
18739
+ * réordonne pas les dates, ne corrige pas les chevauchements. Il résout, indexe et signale.
18740
+ */
18741
+ const LINK_TYPES = ['FS', 'SS', 'FF', 'SF'];
18742
+ const clamp01 = (v) => {
18743
+ const n = typeof v === 'number' ? v : Number(v);
18744
+ return Number.isFinite(n) ? Math.max(0, Math.min(1, n)) : 0;
18745
+ };
18746
+ /**
18747
+ * Résout un document brut : dates coercées, durées converties par le calendrier, hiérarchie
18748
+ * établie, rangs complétés, liens validés.
18749
+ */
18750
+ function cadNormalizePlan(plan, options = {}) {
18751
+ const issues = [];
18752
+ const add = (level, code, message, ref) => {
18753
+ issues.push({ level, code, message, ref });
18754
+ };
18755
+ const calendar = new CadWorkCalendar(plan?.calendar);
18756
+ if (calendar.empty)
18757
+ add('error', 'empty-calendar', 'Aucun temps ouvré déclaré : les durées ne peuvent pas être converties.');
18758
+ const resources = (plan?.resources ?? []).filter(r => r && typeof r.id === 'string');
18759
+ const resourceIds = new Set(resources.map(r => r.id));
18760
+ const buckets = [...(plan?.buckets ?? [])]
18761
+ .filter(b => b && typeof b.id === 'string')
18762
+ .map((b, i) => ({ ...b, order: b.order ?? i }))
18763
+ .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
18764
+ const bucketIds = new Set(buckets.map(b => b.id));
18765
+ /* ---- 1. index des tâches, doublons écartés ---- */
18766
+ const raw = new Map();
18767
+ for (const t of plan?.tasks ?? []) {
18768
+ if (!t || typeof t.id !== 'string' || !t.id)
18769
+ continue;
18770
+ if (raw.has(t.id)) {
18771
+ add('error', 'duplicate-task', 'Identifiant en double : la première occurrence est retenue.', t.id);
18772
+ continue;
18773
+ }
18774
+ raw.set(t.id, t);
18775
+ }
18776
+ /* ---- 2. tâches résolues, hors hiérarchie et hors rollup ---- */
18777
+ const tasks = new Map();
18778
+ for (const [id, t] of raw) {
18779
+ let start = toDate(t.start ?? null);
18780
+ let end = toDate(t.end ?? null);
18781
+ const milestone = !!t.milestone;
18782
+ if (t.start != null && t.start !== '' && !start)
18783
+ add('warning', 'bad-date', 'Date de début illisible.', id);
18784
+ if (t.end != null && t.end !== '' && !end)
18785
+ add('warning', 'bad-date', 'Date de fin illisible.', id);
18786
+ if (milestone) {
18787
+ end = start;
18788
+ }
18789
+ else if (t.duration) {
18790
+ const d = cadParseDuration(t.duration);
18791
+ if (!d)
18792
+ add('warning', 'bad-duration', 'Durée illisible : ' + t.duration, id);
18793
+ else {
18794
+ const minutes = cadDurationToMinutes(d, calendar.dailyMinutes, calendar.weekDays);
18795
+ if (start && !end)
18796
+ end = calendar.addMinutes(start, minutes);
18797
+ else if (!start && end)
18798
+ start = calendar.addMinutes(end, -minutes);
18799
+ }
18800
+ }
18801
+ if (start && end && end.getTime() < start.getTime()) {
18802
+ add('warning', 'end-before-start', 'La fin précède le début : la fin est ramenée au début.', id);
18803
+ end = start;
18804
+ }
18805
+ const assignments = (t.assignments ?? [])
18806
+ .filter(a => a && typeof a.resource === 'string')
18807
+ .map(a => {
18808
+ if (!resourceIds.has(a.resource))
18809
+ add('warning', 'unknown-resource', 'Ressource inconnue : ' + a.resource, id);
18810
+ return { resource: a.resource, units: Number.isFinite(a.units) ? a.units : 1 };
18811
+ });
18812
+ if (t.bucket && !bucketIds.has(t.bucket))
18813
+ add('warning', 'unknown-bucket', 'Colonne inconnue : ' + t.bucket, id);
18814
+ tasks.set(id, {
18815
+ id,
18816
+ label: t.label ?? id,
18817
+ parent: null,
18818
+ start,
18819
+ end,
18820
+ milestone,
18821
+ bucket: t.bucket ?? null,
18822
+ rank: cadIsRank(t.rank) ? t.rank : '',
18823
+ assignments,
18824
+ progress: clamp01(t.progress),
18825
+ tags: [...(t.tags ?? [])],
18826
+ color: t.color ?? null,
18827
+ locked: !!t.locked,
18828
+ rrule: t.rrule ?? null,
18829
+ exdates: [...(t.exdates ?? [])],
18830
+ masterId: t.masterId ?? null,
18831
+ recurrenceId: toDate(t.recurrenceId ?? null),
18832
+ level: 0,
18833
+ children: [],
18834
+ scheduled: false,
18835
+ rolled: false,
18836
+ recurring: !!t.rrule,
18837
+ meta: t.meta ?? {},
18838
+ source: t,
18839
+ });
18840
+ }
18841
+ /* ---- 3. hiérarchie : parent inconnu, auto-parent et cycles deviennent des racines ---- */
18842
+ for (const [id, t] of raw) {
18843
+ const node = tasks.get(id);
18844
+ const parent = t.parent ?? null;
18845
+ if (!parent)
18846
+ continue;
18847
+ if (parent === id) {
18848
+ add('error', 'self-parent', 'Une tâche ne peut pas être son propre parent.', id);
18849
+ continue;
18850
+ }
18851
+ if (!tasks.has(parent)) {
18852
+ add('warning', 'unknown-parent', 'Parent inconnu : ' + parent + ' — la tâche devient racine.', id);
18853
+ continue;
18854
+ }
18855
+ node.parent = parent;
18856
+ }
18857
+ for (const node of tasks.values()) {
18858
+ const seen = new Set([node.id]);
18859
+ let cur = node.parent;
18860
+ while (cur) {
18861
+ if (seen.has(cur)) {
18862
+ add('error', 'parent-cycle', 'Cycle de parenté rompu à cette tâche.', node.id);
18863
+ node.parent = null;
18864
+ break;
18865
+ }
18866
+ seen.add(cur);
18867
+ cur = tasks.get(cur)?.parent ?? null;
18868
+ }
18869
+ }
18870
+ /* ---- 4. fratries et rangs : les rangs existants font l'ordre, les manquants sont complétés ---- */
18871
+ const groups = new Map();
18872
+ for (const id of tasks.keys()) {
18873
+ const key = tasks.get(id).parent;
18874
+ const list = groups.get(key);
18875
+ if (list)
18876
+ list.push(id);
18877
+ else
18878
+ groups.set(key, [id]);
18879
+ }
18880
+ for (const [key, ids] of groups) {
18881
+ const ranked = ids.filter(i => tasks.get(i).rank).sort((a, b) => (tasks.get(a).rank < tasks.get(b).rank ? -1 : 1));
18882
+ const unranked = ids.filter(i => !tasks.get(i).rank);
18883
+ const ordered = [...ranked, ...unranked];
18884
+ for (let i = 0; i < ordered.length; i++) {
18885
+ const node = tasks.get(ordered[i]);
18886
+ if (node.rank)
18887
+ continue;
18888
+ node.rank = cadRankBetween(i > 0 ? tasks.get(ordered[i - 1]).rank : null, null);
18889
+ }
18890
+ groups.set(key, ordered);
18891
+ if (key !== null)
18892
+ tasks.get(key).children = ordered;
18893
+ }
18894
+ /* ---- 5. ordre final : parcours en profondeur, niveaux ---- */
18895
+ const roots = groups.get(null) ?? [];
18896
+ const order = [];
18897
+ const walk = (ids, level) => {
18898
+ for (const id of ids) {
18899
+ const node = tasks.get(id);
18900
+ node.level = level;
18901
+ order.push(node);
18902
+ if (node.children.length)
18903
+ walk(node.children, level + 1);
18904
+ }
18905
+ };
18906
+ walk(roots, 0);
18907
+ /* ---- 6. barres récapitulatives : un parent sans dates prend l'étendue de ses descendants ---- */
18908
+ if (options.rollup !== false) {
18909
+ for (let i = order.length - 1; i >= 0; i--) {
18910
+ const node = order[i];
18911
+ if (!node.children.length || (node.start && node.end))
18912
+ continue;
18913
+ let lo = null;
18914
+ let hi = null;
18915
+ for (const cid of node.children) {
18916
+ const c = tasks.get(cid);
18917
+ if (c.start)
18918
+ lo = lo === null ? c.start.getTime() : Math.min(lo, c.start.getTime());
18919
+ if (c.end)
18920
+ hi = hi === null ? c.end.getTime() : Math.max(hi, c.end.getTime());
18921
+ }
18922
+ if (lo !== null && hi !== null) {
18923
+ node.start = new Date(lo);
18924
+ node.end = new Date(hi);
18925
+ node.rolled = true;
18926
+ }
18927
+ }
18928
+ }
18929
+ for (const node of order)
18930
+ node.scheduled = !!(node.start && node.end);
18931
+ /* ---- 7. liens ---- */
18932
+ const links = [];
18933
+ const seenLink = new Set();
18934
+ for (const l of plan?.links ?? []) {
18935
+ if (!l || typeof l.from !== 'string' || typeof l.to !== 'string')
18936
+ continue;
18937
+ if (l.from === l.to) {
18938
+ add('error', 'self-link', 'Une tâche ne peut pas dépendre d\'elle-même.', l.from);
18939
+ continue;
18940
+ }
18941
+ if (!tasks.has(l.from) || !tasks.has(l.to)) {
18942
+ add('warning', 'unknown-link-end', 'Lien ignoré : ' + l.from + ' -> ' + l.to + ' vise une tâche absente.', l.id);
18943
+ continue;
18944
+ }
18945
+ const key = l.from + '>' + l.to;
18946
+ if (seenLink.has(key))
18947
+ continue;
18948
+ seenLink.add(key);
18949
+ const d = l.lag ? cadParseDuration(l.lag) : null;
18950
+ if (l.lag && !d)
18951
+ add('warning', 'bad-duration', 'Décalage illisible : ' + l.lag, l.id);
18952
+ links.push({
18953
+ id: l.id ?? key,
18954
+ from: l.from,
18955
+ to: l.to,
18956
+ type: LINK_TYPES.includes(l.type) ? l.type : 'FS',
18957
+ lagMinutes: cadDurationToMinutes(d, calendar.dailyMinutes, calendar.weekDays),
18958
+ });
18959
+ }
18960
+ for (const id of cadFindLinkCycle(links)) {
18961
+ add('error', 'link-cycle', 'Cette tâche participe à un cycle de dépendances.', id);
18962
+ }
18963
+ /* ---- 8. étendue ---- */
18964
+ let lo = null;
18965
+ let hi = null;
18966
+ for (const t of order) {
18967
+ if (!t.scheduled)
18968
+ continue;
18969
+ lo = lo === null ? t.start.getTime() : Math.min(lo, t.start.getTime());
18970
+ hi = hi === null ? t.end.getTime() : Math.max(hi, t.end.getTime());
18971
+ }
18972
+ return {
18973
+ tasks: order,
18974
+ byId: tasks,
18975
+ roots,
18976
+ links,
18977
+ resources,
18978
+ buckets,
18979
+ calendar,
18980
+ range: lo !== null && hi !== null ? { start: new Date(lo), end: new Date(hi) } : null,
18981
+ issues,
18982
+ };
18983
+ }
18984
+ /**
18985
+ * Identifiants participant à un cycle de dépendances, par tri topologique (Kahn) :
18986
+ * ce qui ne sort jamais de la file est exactement ce qui est pris dans un cycle.
18987
+ * Nécessaire bien avant le moteur de replanification — un cycle non détecté s'y traduirait
18988
+ * par une boucle infinie plutôt que par un message.
18989
+ */
18990
+ function cadFindLinkCycle(links) {
18991
+ const out = new Map();
18992
+ const indeg = new Map();
18993
+ for (const l of links) {
18994
+ const edges = out.get(l.from);
18995
+ if (edges)
18996
+ edges.push(l.to);
18997
+ else
18998
+ out.set(l.from, [l.to]);
18999
+ if (!indeg.has(l.from))
19000
+ indeg.set(l.from, 0);
19001
+ indeg.set(l.to, (indeg.get(l.to) ?? 0) + 1);
19002
+ }
19003
+ const queue = [...indeg.keys()].filter(k => !indeg.get(k));
19004
+ let seen = 0;
19005
+ while (queue.length) {
19006
+ const id = queue.shift();
19007
+ seen++;
19008
+ for (const next of out.get(id) ?? []) {
19009
+ const n = (indeg.get(next) ?? 0) - 1;
19010
+ indeg.set(next, n);
19011
+ if (n === 0)
19012
+ queue.push(next);
19013
+ }
19014
+ }
19015
+ return seen === indeg.size ? [] : [...indeg.keys()].filter(k => (indeg.get(k) ?? 0) > 0);
19016
+ }
19017
+ /** Tâches qu'aucune vue temporelle ne sait placer — la zone « non planifié », jamais le silence. */
19018
+ const cadUnscheduled = (plan) => plan.tasks.filter(t => !t.scheduled);
19019
+
19020
+ /** Styles de cad-taskboard (présentations cartes et liste). Tokens --cad-* uniquement. */
19021
+ const CAD_TASKBOARD_STYLES = `
19022
+ :host { display: flex; flex-direction: column; min-height: 0; height: 100%; font: var(--cad-font-size)/1.35 var(--cad-font); color: var(--cad-fg); background: var(--cad-bg); }
19023
+
19024
+ /* ---------------- présentation CARTES ---------------- */
19025
+ .tb__board { flex: 1; min-height: 0; display: flex; gap: 12px; padding: 12px; overflow-x: auto; overflow-y: hidden; scrollbar-gutter: stable; }
19026
+ .tb__col { flex: 0 0 var(--cad-taskboard-col, 258px); display: flex; flex-direction: column; min-height: 0; background: var(--cad-bg-subtle); border: 1px solid var(--cad-border); border-radius: var(--cad-radius); }
19027
+ .tb__head { display: flex; align-items: center; gap: 8px; padding: 9px 11px; border-bottom: 1px solid var(--cad-border); }
19028
+ .tb__dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--_c, var(--cad-border-strong)); }
19029
+ .tb__name { font-weight: 500; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
19030
+ .tb__count { font-family: var(--cad-font-mono); font-size: var(--cad-font-size-sm); padding: 2px 7px; border-radius: var(--cad-radius-pill); background: var(--cad-elevated); color: var(--cad-fg-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
19031
+ .tb__count.is-full { background: var(--cad-warning-soft); color: var(--cad-warning); }
19032
+ .tb__count.is-over { background: var(--cad-danger-soft); color: var(--cad-danger); font-weight: 500; }
19033
+
19034
+ .tb__list { flex: 1; min-height: 0; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 8px; scrollbar-gutter: stable; }
19035
+ .tb__list.cdk-drop-list-dragging { background: var(--cad-accent-soft); }
19036
+
19037
+ .tb__card { background: var(--cad-surface); border: 1px solid var(--cad-border); border-radius: var(--cad-radius-sm); padding: 9px 10px; box-shadow: var(--cad-shadow-sm); display: flex; flex-direction: column; gap: 7px; cursor: grab; user-select: none; transition: border-color var(--cad-dur) var(--cad-ease), box-shadow var(--cad-dur) var(--cad-ease); }
19038
+ .tb__card:hover { border-color: var(--cad-border-strong); box-shadow: var(--cad-shadow-md); }
19039
+ .tb__card:focus-visible { outline: 2px solid var(--cad-ring); outline-offset: 1px; }
19040
+ .tb__card.is-orphan { border-left: 2px dashed var(--cad-warning); }
19041
+ :host(.is-static) .tb__card, :host(.is-static) .tb__row { cursor: default; }
19042
+
19043
+ .tb__title { font-weight: 500; line-height: 1.32; }
19044
+ .tb__meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
19045
+ .tb__tag { font-family: var(--cad-font-mono); font-size: 10px; padding: 2px 6px; border-radius: var(--cad-radius-sm); background: var(--cad-elevated); color: var(--cad-fg-secondary); }
19046
+ .tb__due { font-family: var(--cad-font-mono); font-size: var(--cad-font-size-sm); color: var(--cad-fg-muted); font-variant-numeric: tabular-nums; }
19047
+ .tb__due.is-late { color: var(--cad-danger); font-weight: 500; }
19048
+ .tb__sp { flex: 1 1 auto; }
19049
+ .tb__prog { height: 3px; border-radius: var(--cad-radius-pill); background: var(--cad-elevated); overflow: hidden; }
19050
+ .tb__prog i { display: block; height: 100%; border-radius: var(--cad-radius-pill); background: var(--cad-accent); }
19051
+ .tb__prog.is-done i { background: var(--cad-success); }
19052
+
19053
+ .tb__empty { padding: 14px 10px; text-align: center; color: var(--cad-fg-muted); font-size: var(--cad-font-size-sm); border: 1px dashed var(--cad-border-strong); border-radius: var(--cad-radius-sm); }
19054
+
19055
+ /* ---------------- présentation LISTE ---------------- */
19056
+ .tb__rows { flex: 1; min-height: 0; overflow-y: auto; padding-bottom: 12px; scrollbar-gutter: stable; }
19057
+ .tb__grp { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 14px; background: var(--cad-bg-subtle); border-top: 1px solid var(--cad-border); border-bottom: 1px solid var(--cad-border); position: sticky; top: 0; z-index: 1; cursor: pointer; text-align: left; font: inherit; color: inherit; }
19058
+ .tb__grp:hover { background: var(--cad-hover); }
19059
+ .tb__grp:focus-visible { outline: 2px solid var(--cad-ring); outline-offset: -2px; }
19060
+ .tb__grp cad-icon { color: var(--cad-fg-muted); transition: transform var(--cad-dur) var(--cad-ease); }
19061
+ .tb__grp.is-collapsed cad-icon { transform: rotate(-90deg); }
19062
+ .tb__grp b { font-weight: 500; }
19063
+
19064
+ .tb__group-list { display: block; }
19065
+ .tb__row { display: grid; grid-template-columns: 1fr 92px 88px 26px; align-items: center; gap: 12px; padding: 8px 14px; border-bottom: 1px solid var(--cad-border); cursor: grab; user-select: none; background: var(--cad-bg); }
19066
+ .tb__row:hover { background: var(--cad-hover); }
19067
+ .tb__row:focus-visible { outline: 2px solid var(--cad-ring); outline-offset: -2px; }
19068
+ .tb__row.is-orphan { box-shadow: inset 2px 0 0 var(--cad-warning); }
19069
+ .tb__main { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
19070
+ .tb__main .tb__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
19071
+ .tb__pct { display: flex; align-items: center; gap: 7px; }
19072
+ .tb__pct .tb__prog { flex: 1; }
19073
+ .tb__pct span { font-family: var(--cad-font-mono); font-size: 10px; color: var(--cad-fg-muted); font-variant-numeric: tabular-nums; }
19074
+
19075
+ /* sous le seuil, la liste se resserre : l'échéance rejoint le titre, l'avancement s'efface */
19076
+ :host(.is-compact) .tb__row { grid-template-columns: 1fr 26px; }
19077
+ :host(.is-compact) .tb__cell-due, :host(.is-compact) .tb__pct { display: none; }
19078
+ :host(.is-compact) .tb__mob { display: inline-flex; }
19079
+ .tb__mob { display: none; align-items: center; gap: 7px; }
19080
+
19081
+ /* ---------------- glisser (CDK) ---------------- */
19082
+ .cdk-drag-preview { box-shadow: var(--cad-shadow-lg); border-radius: var(--cad-radius-sm); background: var(--cad-overlay); border: 1px solid var(--cad-border); }
19083
+ .cdk-drag-placeholder { opacity: .34; }
19084
+ .cdk-drag-animating { transition: transform 180ms var(--cad-ease); }
19085
+ .tb__list.cdk-drop-list-dragging .tb__card:not(.cdk-drag-placeholder),
19086
+ .tb__group-list.cdk-drop-list-dragging .tb__row:not(.cdk-drag-placeholder) { transition: transform 180ms var(--cad-ease); }
19087
+
19088
+ /* annonce aux lecteurs d'écran — hors flux, jamais display:none (elle ne serait plus lue) */
19089
+ .tb__live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
19090
+
19091
+ @media (prefers-reduced-motion: reduce) { .cdk-drag-animating, .tb__list.cdk-drop-list-dragging .tb__card, .tb__grp cad-icon { transition: none; } }
19092
+ `;
19093
+
19094
+ /** Contenu d'une carte : `<ng-template cadTaskCard let-task let-bucket="bucket">`. */
19095
+ class CadTaskCardDirective {
19096
+ tpl = inject(TemplateRef);
19097
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadTaskCardDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
19098
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.20", type: CadTaskCardDirective, isStandalone: true, selector: "[cadTaskCard]", ngImport: i0 });
19099
+ }
19100
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadTaskCardDirective, decorators: [{
19101
+ type: Directive,
19102
+ args: [{ selector: '[cadTaskCard]', standalone: true }]
19103
+ }] });
19104
+ /** Contenu d'une ligne : `<ng-template cadTaskRow let-task let-group="group">`. */
19105
+ class CadTaskRowDirective {
19106
+ tpl = inject(TemplateRef);
19107
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadTaskRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
19108
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.20", type: CadTaskRowDirective, isStandalone: true, selector: "[cadTaskRow]", ngImport: i0 });
19109
+ }
19110
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadTaskRowDirective, decorators: [{
19111
+ type: Directive,
19112
+ args: [{ selector: '[cadTaskRow]', standalone: true }]
19113
+ }] });
19114
+ const EMPTY_PATCH = { src: undefined, map: new Map() };
19115
+ const NO_RESOURCE = ' none';
19116
+ /**
19117
+ * <cad-taskboard [plan]="plan" mode="auto" groupBy="bucket" (taskMove) (taskAssign) (taskOpen) (wipExceeded)>
19118
+ * <ng-template cadTaskCard let-t>…</ng-template>
19119
+ * <ng-template cadTaskRow let-t>…</ng-template>
19120
+ * </cad-taskboard>
19121
+ *
19122
+ * UNE SEULE ENTRÉE sépare les deux présentations : `mode`. On transforme la présentation, jamais
19123
+ * l'architecture de l'information — mêmes tâches, même rang, même geste, même intention. `auto`
19124
+ * bascule en liste sous le seuil mobile, sur le patron de `cad-nav-drawer`.
19125
+ *
19126
+ * LE COMPOSANT N'ÉCRIT JAMAIS. Il émet `taskMove` / `taskAssign` et laisse l'application décider :
19127
+ * c'est ce qui rend la source indifférente (slapi ou document statique). Pour que le geste reste
19128
+ * vivant sans mentir, il conserve un CORRECTIF LOCAL optimiste, abandonné dès qu'un nouveau `plan`
19129
+ * arrive — l'entrée n'est jamais mutée, et l'affichage suit l'application si elle refuse.
19130
+ *
19131
+ * La limite de travail en cours ALERTE mais ne bloque pas : un tableau qui refuse un geste sans
19132
+ * l'expliquer se contourne au lieu d'être lu.
19133
+ *
19134
+ * Clavier : Entrée / Espace ouvrent la tâche ; Ctrl (ou Cmd) + flèches la déplacent — gauche et
19135
+ * droite changent de colonne, haut et bas changent de rang. Chaque déplacement est annoncé.
19136
+ */
19137
+ class CadTaskboardComponent {
19138
+ /** Document de planification brut ; il est normalisé en interne et jamais muté. */
19139
+ plan = input(null, ...(ngDevMode ? [{ debugName: "plan" }] : /* istanbul ignore next */ []));
19140
+ /** 'board' = cartes, 'list' = lignes groupées, 'auto' = liste sous le seuil mobile. */
19141
+ mode = input('auto', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
19142
+ /** Regroupement de la présentation liste. Par personne, le glisser réaffecte au lieu de ranger. */
19143
+ groupBy = input('bucket', ...(ngDevMode ? [{ debugName: "groupBy" }] : /* istanbul ignore next */ []));
19144
+ dragDisabled = input(false, { ...(ngDevMode ? { debugName: "dragDisabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
19145
+ /** Groupes repliés (présentation liste). */
19146
+ collapsedKeys = model(new Set(), ...(ngDevMode ? [{ debugName: "collapsedKeys" }] : /* istanbul ignore next */ []));
19147
+ emptyText = input('Rien ici', ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
19148
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
19149
+ /** Format d'échéance (jetons de `formatDate`). */
19150
+ dateFormat = input('dd/MM', ...(ngDevMode ? [{ debugName: "dateFormat" }] : /* istanbul ignore next */ []));
19151
+ /** Instant de référence pour « en retard ». Explicite en test, sinon maintenant. */
19152
+ today = input(null, ...(ngDevMode ? [{ debugName: "today" }] : /* istanbul ignore next */ []));
19153
+ taskMove = output();
19154
+ taskAssign = output();
19155
+ taskOpen = output();
19156
+ wipExceeded = output();
19157
+ cardTpl = contentChild(CadTaskCardDirective, ...(ngDevMode ? [{ debugName: "cardTpl" }] : /* istanbul ignore next */ []));
19158
+ rowTpl = contentChild(CadTaskRowDirective, ...(ngDevMode ? [{ debugName: "rowTpl" }] : /* istanbul ignore next */ []));
19159
+ orphanHint = 'Sans colonne connue — rangée par défaut, jamais escamotée';
19160
+ announcement = signal('', ...(ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []));
19161
+ bp = inject(CadBreakpointService);
19162
+ patch = signal(EMPTY_PATCH, ...(ngDevMode ? [{ debugName: "patch" }] : /* istanbul ignore next */ []));
19163
+ /** Document résolu par le socle de planification. */
19164
+ normalized = computed(() => cadNormalizePlan(this.plan()), ...(ngDevMode ? [{ debugName: "normalized" }] : /* istanbul ignore next */ []));
19165
+ /** Présentation effective, une fois `auto` tranché par le seuil. */
19166
+ view = computed(() => {
19167
+ const m = this.mode();
19168
+ return m === 'auto' ? (this.bp.isMobile() ? 'list' : 'board') : m;
19169
+ }, ...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
19170
+ compact = computed(() => this.view() === 'list' && this.bp.isMobile(), ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
19171
+ /** Tâches après application du correctif optimiste (abandonné dès que `plan` change). */
19172
+ tasks = computed(() => {
19173
+ const base = this.normalized().tasks;
19174
+ const p = this.patch();
19175
+ if (p.src !== this.plan() || !p.map.size)
19176
+ return base;
19177
+ return base.map(t => {
19178
+ const o = p.map.get(t.id);
19179
+ if (!o)
19180
+ return t;
19181
+ const next = { ...t, rank: o.rank };
19182
+ if (o.bucket !== undefined)
19183
+ next.bucket = o.bucket;
19184
+ if (o.resource !== undefined) {
19185
+ next.assignments = o.resource ? [{ resource: o.resource, units: t.assignments[0]?.units ?? 1 }] : [];
19186
+ }
19187
+ return next;
19188
+ });
19189
+ }, ...(ngDevMode ? [{ debugName: "tasks" }] : /* istanbul ignore next */ []));
19190
+ /** Colonnes déclarées, ou une colonne implicite : un tableau sans colonne reste un tableau. */
19191
+ declared = computed(() => {
19192
+ const b = this.normalized().buckets;
19193
+ return b.length ? b : [{ id: '', label: 'Tâches' }];
19194
+ }, ...(ngDevMode ? [{ debugName: "declared" }] : /* istanbul ignore next */ []));
19195
+ /** Colonnes (cartes, ou liste groupée par colonne) ou personnes (liste groupée par personne). */
19196
+ columns = computed(() => {
19197
+ const tasks = this.tasks();
19198
+ const byRank = (a, b) => (a.rank < b.rank ? -1 : a.rank > b.rank ? 1 : 0);
19199
+ if (this.view() === 'list' && this.groupBy() === 'resource') {
19200
+ const res = this.normalized().resources;
19201
+ const known = new Set(res.map(r => r.id));
19202
+ const groups = res.map(r => ({
19203
+ id: r.id,
19204
+ label: r['label'] ?? r.id,
19205
+ color: r.color ?? null,
19206
+ wipLimit: null,
19207
+ tasks: tasks.filter(t => t.assignments[0]?.resource === r.id).sort(byRank),
19208
+ }));
19209
+ groups.push({
19210
+ id: NO_RESOURCE,
19211
+ label: 'Non affectées',
19212
+ color: null,
19213
+ wipLimit: null,
19214
+ tasks: tasks.filter(t => !t.assignments.length || !known.has(t.assignments[0].resource)).sort(byRank),
19215
+ });
19216
+ return groups;
19217
+ }
19218
+ return this.declared().map((b, i) => ({
19219
+ id: b.id,
19220
+ label: b.label ?? b.id,
19221
+ color: b.color ?? null,
19222
+ wipLimit: b.wipLimit ?? null,
19223
+ tasks: tasks.filter(t => (this.isOrphan(t) ? i === 0 : t.bucket === b.id)).sort(byRank),
19224
+ }));
19225
+ }, ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
19226
+ /* ---------------- lecture ---------------- */
19227
+ /** Colonne réelle d'une tâche : la sienne si elle existe, sinon la première. Jamais « nulle part ». */
19228
+ bucketOf(t) {
19229
+ const declared = this.declared();
19230
+ return declared.some(b => b.id === t.bucket) ? t.bucket : declared[0].id;
19231
+ }
19232
+ isOrphan(t) {
19233
+ return !this.declared().some(b => b.id === t.bucket);
19234
+ }
19235
+ countOf(c) {
19236
+ return c.wipLimit == null ? String(c.tasks.length) : c.tasks.length + '/' + c.wipLimit;
19237
+ }
19238
+ wipState(c) {
19239
+ if (c.wipLimit == null)
19240
+ return 'none';
19241
+ return c.tasks.length > c.wipLimit ? 'over' : c.tasks.length === c.wipLimit ? 'full' : 'none';
19242
+ }
19243
+ due(t) {
19244
+ return t.end ? formatDate(t.end, this.dateFormat()) : '';
19245
+ }
19246
+ isLate(t) {
19247
+ return !!t.end && t.progress < 1 && t.end.getTime() < (this.today() ?? new Date()).getTime();
19248
+ }
19249
+ pct(t) { return Math.round(t.progress * 100); }
19250
+ labelOfResource(id) {
19251
+ const r = this.normalized().resources.find(x => x.id === id);
19252
+ return (r?.['label'] ?? id).slice(0, 24);
19253
+ }
19254
+ /** Initiales d'une personne : « S. Zuccatti » donne SZ, « Charles » donne CH. */
19255
+ initialsOfResource(id) {
19256
+ const mots = this.labelOfResource(id).split(/[\s.'-]+/).filter(Boolean);
19257
+ return (mots.length > 1 ? mots[0][0] + mots[1][0] : (mots[0] ?? id).slice(0, 2)).toUpperCase();
19258
+ }
19259
+ colorOfResource(id) {
19260
+ return this.normalized().resources.find(x => x.id === id)?.color ?? null;
19261
+ }
19262
+ isCollapsed(key) { return this.collapsedKeys().has(key); }
19263
+ toggle(key) {
19264
+ const next = new Set(this.collapsedKeys());
19265
+ if (next.has(key))
19266
+ next.delete(key);
19267
+ else
19268
+ next.add(key);
19269
+ this.collapsedKeys.set(next);
19270
+ }
19271
+ open(t, event) {
19272
+ this.taskOpen.emit({ id: t.id, task: t, event });
19273
+ }
19274
+ /* ---------------- déplacement ---------------- */
19275
+ onDrop(e, kind) {
19276
+ const same = e.previousContainer === e.container;
19277
+ this.apply(e.item.data, e.container.data, e.currentIndex, kind, same ? e.previousIndex : -1);
19278
+ }
19279
+ onKey(e, t, colId) {
19280
+ if (e.key === 'Enter' || e.key === ' ') {
19281
+ e.preventDefault();
19282
+ this.open(t, e);
19283
+ return;
19284
+ }
19285
+ if ((!e.ctrlKey && !e.metaKey) || this.dragDisabled() || t.locked)
19286
+ return;
19287
+ const cols = this.columns();
19288
+ const ci = cols.findIndex(c => c.id === colId);
19289
+ if (ci < 0)
19290
+ return;
19291
+ const here = cols[ci].tasks.findIndex(x => x.id === t.id);
19292
+ const kind = this.view() === 'list' ? this.groupBy() : 'bucket';
19293
+ if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
19294
+ const to = here + (e.key === 'ArrowUp' ? -1 : 1);
19295
+ if (to < 0 || to >= cols[ci].tasks.length)
19296
+ return;
19297
+ e.preventDefault();
19298
+ this.apply(t.id, colId, to, kind, here);
19299
+ }
19300
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
19301
+ const target = cols[ci + (e.key === 'ArrowLeft' ? -1 : 1)];
19302
+ if (!target)
19303
+ return;
19304
+ e.preventDefault();
19305
+ this.apply(t.id, target.id, Math.min(here, target.tasks.length), kind, -1);
19306
+ }
19307
+ }
19308
+ /**
19309
+ * Calcule le nouveau rang entre les deux voisins, pose le correctif optimiste et ÉMET.
19310
+ * `fromIndex` >= 0 signale un déplacement dans le même groupe : le voisinage se lit alors sur
19311
+ * la liste privée de la tâche déplacée, exactement comme le fait `moveItemInArray`.
19312
+ */
19313
+ apply(id, target, index, kind, fromIndex) {
19314
+ const task = this.tasks().find(t => t.id === id);
19315
+ const column = this.columns().find(c => c.id === target);
19316
+ if (!task || !column)
19317
+ return;
19318
+ const items = column.tasks.filter(t => t.id !== id);
19319
+ const at = Math.max(0, Math.min(index, items.length));
19320
+ const rank = cadRankBetween(items[at - 1]?.rank ?? null, items[at]?.rank ?? null);
19321
+ const previousRank = task.rank;
19322
+ if (fromIndex === at)
19323
+ return;
19324
+ const current = this.patch();
19325
+ const map = new Map(current.src === this.plan() ? current.map : []);
19326
+ if (kind === 'resource') {
19327
+ const from = task.assignments[0]?.resource ?? null;
19328
+ const to = target === NO_RESOURCE ? null : target;
19329
+ map.set(id, { ...map.get(id), resource: to, rank });
19330
+ this.patch.set({ src: this.plan(), map });
19331
+ this.taskAssign.emit({ id, task, from, to, rank });
19332
+ this.announce(task, target, column.label, at, 'affectée à ');
19333
+ return;
19334
+ }
19335
+ const from = this.bucketOf(task);
19336
+ map.set(id, { ...map.get(id), bucket: target, rank });
19337
+ this.patch.set({ src: this.plan(), map });
19338
+ this.taskMove.emit({ id, task, from, to: target, rank, previousRank, index: at });
19339
+ this.announce(task, target, column.label, at, 'déplacée dans ');
19340
+ const after = this.columns().find(c => c.id === target);
19341
+ if (after && after.wipLimit != null && after.tasks.length > after.wipLimit) {
19342
+ this.wipExceeded.emit({ bucket: target, limit: after.wipLimit, count: after.tasks.length });
19343
+ }
19344
+ }
19345
+ /** L'annonce ne dit pas « déplacé » : elle dit OÙ et À QUELLE PLACE — la seule information utile sans la vue. */
19346
+ announce(task, key, label, at, verb) {
19347
+ const total = this.columns().find(c => c.id === key)?.tasks.length ?? 0;
19348
+ this.announcement.set(task.label + ' — ' + verb + label + ', position ' + (at + 1) + ' sur ' + total + '.');
19349
+ }
19350
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadTaskboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19351
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: CadTaskboardComponent, isStandalone: true, selector: "cad-taskboard", inputs: { plan: { classPropertyName: "plan", publicName: "plan", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, dragDisabled: { classPropertyName: "dragDisabled", publicName: "dragDisabled", isSignal: true, isRequired: false, transformFunction: null }, collapsedKeys: { classPropertyName: "collapsedKeys", publicName: "collapsedKeys", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, dateFormat: { classPropertyName: "dateFormat", publicName: "dateFormat", isSignal: true, isRequired: false, transformFunction: null }, today: { classPropertyName: "today", publicName: "today", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsedKeys: "collapsedKeysChange", taskMove: "taskMove", taskAssign: "taskAssign", taskOpen: "taskOpen", wipExceeded: "wipExceeded" }, host: { properties: { "class.is-compact": "compact()", "class.is-static": "dragDisabled()", "attr.aria-label": "ariaLabel() || null" }, classAttribute: "cad-taskboard" }, queries: [{ propertyName: "cardTpl", first: true, predicate: CadTaskCardDirective, descendants: true, isSignal: true }, { propertyName: "rowTpl", first: true, predicate: CadTaskRowDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
19352
+ <p class="tb__live" role="status" aria-live="polite">{{ announcement() }}</p>
19353
+
19354
+ @if (view() === 'board') {
19355
+ <div class="tb__board" cdkDropListGroup>
19356
+ @for (col of columns(); track col.id) {
19357
+ <section class="tb__col">
19358
+ <header class="tb__head">
19359
+ <span class="tb__dot" [style.--_c]="col.color"></span>
19360
+ <span class="tb__name">{{ col.label }}</span>
19361
+ <span class="tb__count" [class.is-full]="wipState(col) === 'full'" [class.is-over]="wipState(col) === 'over'"
19362
+ [attr.title]="col.wipLimit != null ? 'Limite de travail en cours' : null">{{ countOf(col) }}</span>
19363
+ </header>
19364
+ <div class="tb__list" cdkDropList [cdkDropListData]="col.id" [cdkDropListDisabled]="dragDisabled()"
19365
+ (cdkDropListDropped)="onDrop($event, 'bucket')" [attr.aria-label]="col.label">
19366
+ @for (t of col.tasks; track t.id) {
19367
+ <article class="tb__card" cdkDrag [cdkDragData]="t.id" [cdkDragDisabled]="dragDisabled() || t.locked"
19368
+ tabindex="0" role="button" [attr.data-id]="t.id" [class.is-orphan]="isOrphan(t)"
19369
+ [attr.title]="isOrphan(t) ? orphanHint : null"
19370
+ (click)="open(t, $event)" (keydown)="onKey($event, t, col.id)">
19371
+ @if (cardTpl()) {
19372
+ <ng-container *ngTemplateOutlet="cardTpl()!.tpl; context: { $implicit: t, bucket: col.id }"/>
19373
+ } @else {
19374
+ <p class="tb__title">{{ t.label }}</p>
19375
+ @if (t.progress > 0) {<div class="tb__prog" [class.is-done]="t.progress >= 1"><i [style.width.%]="t.progress * 100"></i></div>}
19376
+ <div class="tb__meta">
19377
+ @for (g of t.tags; track g) {<span class="tb__tag">{{ g }}</span>}
19378
+ <span class="tb__sp"></span>
19379
+ @if (t.end) {<span class="tb__due" [class.is-late]="isLate(t)" title="Échéance">{{ due(t) }}</span>}
19380
+ @for (a of t.assignments; track a.resource) {
19381
+ <cad-avatar size="sm" [text]="initialsOfResource(a.resource)" [color]="colorOfResource(a.resource)" [title]="labelOfResource(a.resource)"/>
19382
+ }
19383
+ </div>
19384
+ }
19385
+ </article>
19386
+ } @empty {
19387
+ <p class="tb__empty">{{ emptyText() }}</p>
19388
+ }
19389
+ </div>
19390
+ </section>
19391
+ }
19392
+ </div>
19393
+ } @else {
19394
+ <div class="tb__rows" cdkDropListGroup>
19395
+ @for (col of columns(); track col.id) {
19396
+ <button type="button" class="tb__grp" [class.is-collapsed]="isCollapsed(col.id)"
19397
+ [attr.aria-expanded]="!isCollapsed(col.id)" (click)="toggle(col.id)">
19398
+ <cad-icon name="chevron-down" [size]="14"/>
19399
+ <span class="tb__dot" [style.--_c]="col.color"></span>
19400
+ <b>{{ col.label }}</b>
19401
+ <span class="tb__sp"></span>
19402
+ <span class="tb__count" [class.is-full]="wipState(col) === 'full'" [class.is-over]="wipState(col) === 'over'">{{ countOf(col) }}</span>
19403
+ </button>
19404
+ @if (!isCollapsed(col.id)) {
19405
+ <div class="tb__group-list" cdkDropList [cdkDropListData]="col.id" [cdkDropListDisabled]="dragDisabled()"
19406
+ (cdkDropListDropped)="onDrop($event, groupBy())" [attr.aria-label]="col.label">
19407
+ @for (t of col.tasks; track t.id) {
19408
+ <div class="tb__row" cdkDrag [cdkDragData]="t.id" [cdkDragDisabled]="dragDisabled() || t.locked"
19409
+ tabindex="0" role="button" [attr.data-id]="t.id" [class.is-orphan]="isOrphan(t)"
19410
+ [attr.title]="isOrphan(t) ? orphanHint : null"
19411
+ (click)="open(t, $event)" (keydown)="onKey($event, t, col.id)">
19412
+ @if (rowTpl()) {
19413
+ <ng-container *ngTemplateOutlet="rowTpl()!.tpl; context: { $implicit: t, group: col.id }"/>
19414
+ } @else {
19415
+ <div class="tb__main">
19416
+ <span class="tb__title">{{ t.label }}</span>
19417
+ <div class="tb__meta">
19418
+ @for (g of t.tags; track g) {<span class="tb__tag">{{ g }}</span>}
19419
+ @if (t.end) {<span class="tb__mob tb__due" [class.is-late]="isLate(t)">{{ due(t) }}</span>}
19420
+ </div>
19421
+ </div>
19422
+ <span class="tb__due tb__cell-due" [class.is-late]="isLate(t)">{{ t.end ? due(t) : '—' }}</span>
19423
+ <div class="tb__pct">
19424
+ <div class="tb__prog" [class.is-done]="t.progress >= 1"><i [style.width.%]="t.progress * 100"></i></div>
19425
+ <span>{{ pct(t) }}%</span>
19426
+ </div>
19427
+ @if (t.assignments.length) {
19428
+ <cad-avatar size="sm" [text]="initialsOfResource(t.assignments[0].resource)"
19429
+ [color]="colorOfResource(t.assignments[0].resource)" [title]="labelOfResource(t.assignments[0].resource)"/>
19430
+ } @else {<span class="tb__due">—</span>}
19431
+ }
19432
+ </div>
19433
+ } @empty {
19434
+ <p class="tb__empty" style="margin: 10px 14px">{{ emptyText() }}</p>
19435
+ }
19436
+ </div>
19437
+ }
19438
+ }
19439
+ </div>
19440
+ }
19441
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;min-height:0;height:100%;font:var(--cad-font-size)/1.35 var(--cad-font);color:var(--cad-fg);background:var(--cad-bg)}.tb__board{flex:1;min-height:0;display:flex;gap:12px;padding:12px;overflow-x:auto;overflow-y:hidden;scrollbar-gutter:stable}.tb__col{flex:0 0 var(--cad-taskboard-col, 258px);display:flex;flex-direction:column;min-height:0;background:var(--cad-bg-subtle);border:1px solid var(--cad-border);border-radius:var(--cad-radius)}.tb__head{display:flex;align-items:center;gap:8px;padding:9px 11px;border-bottom:1px solid var(--cad-border)}.tb__dot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--_c, var(--cad-border-strong))}.tb__name{font-weight:500;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tb__count{font-family:var(--cad-font-mono);font-size:var(--cad-font-size-sm);padding:2px 7px;border-radius:var(--cad-radius-pill);background:var(--cad-elevated);color:var(--cad-fg-muted);font-variant-numeric:tabular-nums;white-space:nowrap}.tb__count.is-full{background:var(--cad-warning-soft);color:var(--cad-warning)}.tb__count.is-over{background:var(--cad-danger-soft);color:var(--cad-danger);font-weight:500}.tb__list{flex:1;min-height:0;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:8px;scrollbar-gutter:stable}.tb__list.cdk-drop-list-dragging{background:var(--cad-accent-soft)}.tb__card{background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:var(--cad-radius-sm);padding:9px 10px;box-shadow:var(--cad-shadow-sm);display:flex;flex-direction:column;gap:7px;cursor:grab;-webkit-user-select:none;user-select:none;transition:border-color var(--cad-dur) var(--cad-ease),box-shadow var(--cad-dur) var(--cad-ease)}.tb__card:hover{border-color:var(--cad-border-strong);box-shadow:var(--cad-shadow-md)}.tb__card:focus-visible{outline:2px solid var(--cad-ring);outline-offset:1px}.tb__card.is-orphan{border-left:2px dashed var(--cad-warning)}:host(.is-static) .tb__card,:host(.is-static) .tb__row{cursor:default}.tb__title{font-weight:500;line-height:1.32}.tb__meta{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.tb__tag{font-family:var(--cad-font-mono);font-size:10px;padding:2px 6px;border-radius:var(--cad-radius-sm);background:var(--cad-elevated);color:var(--cad-fg-secondary)}.tb__due{font-family:var(--cad-font-mono);font-size:var(--cad-font-size-sm);color:var(--cad-fg-muted);font-variant-numeric:tabular-nums}.tb__due.is-late{color:var(--cad-danger);font-weight:500}.tb__sp{flex:1 1 auto}.tb__prog{height:3px;border-radius:var(--cad-radius-pill);background:var(--cad-elevated);overflow:hidden}.tb__prog i{display:block;height:100%;border-radius:var(--cad-radius-pill);background:var(--cad-accent)}.tb__prog.is-done i{background:var(--cad-success)}.tb__empty{padding:14px 10px;text-align:center;color:var(--cad-fg-muted);font-size:var(--cad-font-size-sm);border:1px dashed var(--cad-border-strong);border-radius:var(--cad-radius-sm)}.tb__rows{flex:1;min-height:0;overflow-y:auto;padding-bottom:12px;scrollbar-gutter:stable}.tb__grp{display:flex;align-items:center;gap:8px;width:100%;padding:8px 14px;background:var(--cad-bg-subtle);border-top:1px solid var(--cad-border);border-bottom:1px solid var(--cad-border);position:sticky;top:0;z-index:1;cursor:pointer;text-align:left;font:inherit;color:inherit}.tb__grp:hover{background:var(--cad-hover)}.tb__grp:focus-visible{outline:2px solid var(--cad-ring);outline-offset:-2px}.tb__grp cad-icon{color:var(--cad-fg-muted);transition:transform var(--cad-dur) var(--cad-ease)}.tb__grp.is-collapsed cad-icon{transform:rotate(-90deg)}.tb__grp b{font-weight:500}.tb__group-list{display:block}.tb__row{display:grid;grid-template-columns:1fr 92px 88px 26px;align-items:center;gap:12px;padding:8px 14px;border-bottom:1px solid var(--cad-border);cursor:grab;-webkit-user-select:none;user-select:none;background:var(--cad-bg)}.tb__row:hover{background:var(--cad-hover)}.tb__row:focus-visible{outline:2px solid var(--cad-ring);outline-offset:-2px}.tb__row.is-orphan{box-shadow:inset 2px 0 0 var(--cad-warning)}.tb__main{min-width:0;display:flex;flex-direction:column;gap:4px}.tb__main .tb__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tb__pct{display:flex;align-items:center;gap:7px}.tb__pct .tb__prog{flex:1}.tb__pct span{font-family:var(--cad-font-mono);font-size:10px;color:var(--cad-fg-muted);font-variant-numeric:tabular-nums}:host(.is-compact) .tb__row{grid-template-columns:1fr 26px}:host(.is-compact) .tb__cell-due,:host(.is-compact) .tb__pct{display:none}:host(.is-compact) .tb__mob{display:inline-flex}.tb__mob{display:none;align-items:center;gap:7px}.cdk-drag-preview{box-shadow:var(--cad-shadow-lg);border-radius:var(--cad-radius-sm);background:var(--cad-overlay);border:1px solid var(--cad-border)}.cdk-drag-placeholder{opacity:.34}.cdk-drag-animating{transition:transform .18s var(--cad-ease)}.tb__list.cdk-drop-list-dragging .tb__card:not(.cdk-drag-placeholder),.tb__group-list.cdk-drop-list-dragging .tb__row:not(.cdk-drag-placeholder){transition:transform .18s var(--cad-ease)}.tb__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}@media(prefers-reduced-motion:reduce){.cdk-drag-animating,.tb__list.cdk-drop-list-dragging .tb__card,.tb__grp cad-icon{transition:none}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDropListGroup, selector: "[cdkDropListGroup]", inputs: ["cdkDropListGroupDisabled"], exportAs: ["cdkDropListGroup"] }, { kind: "component", type: CadAvatarComponent, selector: "cad-avatar", inputs: ["src", "text", "icon", "size", "shape", "color", "label"] }, { kind: "component", type: CadIconComponent, selector: "cad-icon", inputs: ["name", "size", "strokeWidth", "label", "title", "spin", "filled", "flip", "rotate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
19442
+ }
19443
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: CadTaskboardComponent, decorators: [{
19444
+ type: Component,
19445
+ args: [{ selector: 'cad-taskboard', standalone: true, imports: [NgTemplateOutlet, CdkDrag, CdkDropList, CdkDropListGroup, CadAvatarComponent, CadIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
19446
+ class: 'cad-taskboard',
19447
+ '[class.is-compact]': 'compact()',
19448
+ '[class.is-static]': 'dragDisabled()',
19449
+ '[attr.aria-label]': 'ariaLabel() || null',
19450
+ }, template: `
19451
+ <p class="tb__live" role="status" aria-live="polite">{{ announcement() }}</p>
19452
+
19453
+ @if (view() === 'board') {
19454
+ <div class="tb__board" cdkDropListGroup>
19455
+ @for (col of columns(); track col.id) {
19456
+ <section class="tb__col">
19457
+ <header class="tb__head">
19458
+ <span class="tb__dot" [style.--_c]="col.color"></span>
19459
+ <span class="tb__name">{{ col.label }}</span>
19460
+ <span class="tb__count" [class.is-full]="wipState(col) === 'full'" [class.is-over]="wipState(col) === 'over'"
19461
+ [attr.title]="col.wipLimit != null ? 'Limite de travail en cours' : null">{{ countOf(col) }}</span>
19462
+ </header>
19463
+ <div class="tb__list" cdkDropList [cdkDropListData]="col.id" [cdkDropListDisabled]="dragDisabled()"
19464
+ (cdkDropListDropped)="onDrop($event, 'bucket')" [attr.aria-label]="col.label">
19465
+ @for (t of col.tasks; track t.id) {
19466
+ <article class="tb__card" cdkDrag [cdkDragData]="t.id" [cdkDragDisabled]="dragDisabled() || t.locked"
19467
+ tabindex="0" role="button" [attr.data-id]="t.id" [class.is-orphan]="isOrphan(t)"
19468
+ [attr.title]="isOrphan(t) ? orphanHint : null"
19469
+ (click)="open(t, $event)" (keydown)="onKey($event, t, col.id)">
19470
+ @if (cardTpl()) {
19471
+ <ng-container *ngTemplateOutlet="cardTpl()!.tpl; context: { $implicit: t, bucket: col.id }"/>
19472
+ } @else {
19473
+ <p class="tb__title">{{ t.label }}</p>
19474
+ @if (t.progress > 0) {<div class="tb__prog" [class.is-done]="t.progress >= 1"><i [style.width.%]="t.progress * 100"></i></div>}
19475
+ <div class="tb__meta">
19476
+ @for (g of t.tags; track g) {<span class="tb__tag">{{ g }}</span>}
19477
+ <span class="tb__sp"></span>
19478
+ @if (t.end) {<span class="tb__due" [class.is-late]="isLate(t)" title="Échéance">{{ due(t) }}</span>}
19479
+ @for (a of t.assignments; track a.resource) {
19480
+ <cad-avatar size="sm" [text]="initialsOfResource(a.resource)" [color]="colorOfResource(a.resource)" [title]="labelOfResource(a.resource)"/>
19481
+ }
19482
+ </div>
19483
+ }
19484
+ </article>
19485
+ } @empty {
19486
+ <p class="tb__empty">{{ emptyText() }}</p>
19487
+ }
19488
+ </div>
19489
+ </section>
19490
+ }
19491
+ </div>
19492
+ } @else {
19493
+ <div class="tb__rows" cdkDropListGroup>
19494
+ @for (col of columns(); track col.id) {
19495
+ <button type="button" class="tb__grp" [class.is-collapsed]="isCollapsed(col.id)"
19496
+ [attr.aria-expanded]="!isCollapsed(col.id)" (click)="toggle(col.id)">
19497
+ <cad-icon name="chevron-down" [size]="14"/>
19498
+ <span class="tb__dot" [style.--_c]="col.color"></span>
19499
+ <b>{{ col.label }}</b>
19500
+ <span class="tb__sp"></span>
19501
+ <span class="tb__count" [class.is-full]="wipState(col) === 'full'" [class.is-over]="wipState(col) === 'over'">{{ countOf(col) }}</span>
19502
+ </button>
19503
+ @if (!isCollapsed(col.id)) {
19504
+ <div class="tb__group-list" cdkDropList [cdkDropListData]="col.id" [cdkDropListDisabled]="dragDisabled()"
19505
+ (cdkDropListDropped)="onDrop($event, groupBy())" [attr.aria-label]="col.label">
19506
+ @for (t of col.tasks; track t.id) {
19507
+ <div class="tb__row" cdkDrag [cdkDragData]="t.id" [cdkDragDisabled]="dragDisabled() || t.locked"
19508
+ tabindex="0" role="button" [attr.data-id]="t.id" [class.is-orphan]="isOrphan(t)"
19509
+ [attr.title]="isOrphan(t) ? orphanHint : null"
19510
+ (click)="open(t, $event)" (keydown)="onKey($event, t, col.id)">
19511
+ @if (rowTpl()) {
19512
+ <ng-container *ngTemplateOutlet="rowTpl()!.tpl; context: { $implicit: t, group: col.id }"/>
19513
+ } @else {
19514
+ <div class="tb__main">
19515
+ <span class="tb__title">{{ t.label }}</span>
19516
+ <div class="tb__meta">
19517
+ @for (g of t.tags; track g) {<span class="tb__tag">{{ g }}</span>}
19518
+ @if (t.end) {<span class="tb__mob tb__due" [class.is-late]="isLate(t)">{{ due(t) }}</span>}
19519
+ </div>
19520
+ </div>
19521
+ <span class="tb__due tb__cell-due" [class.is-late]="isLate(t)">{{ t.end ? due(t) : '—' }}</span>
19522
+ <div class="tb__pct">
19523
+ <div class="tb__prog" [class.is-done]="t.progress >= 1"><i [style.width.%]="t.progress * 100"></i></div>
19524
+ <span>{{ pct(t) }}%</span>
19525
+ </div>
19526
+ @if (t.assignments.length) {
19527
+ <cad-avatar size="sm" [text]="initialsOfResource(t.assignments[0].resource)"
19528
+ [color]="colorOfResource(t.assignments[0].resource)" [title]="labelOfResource(t.assignments[0].resource)"/>
19529
+ } @else {<span class="tb__due">—</span>}
19530
+ }
19531
+ </div>
19532
+ } @empty {
19533
+ <p class="tb__empty" style="margin: 10px 14px">{{ emptyText() }}</p>
19534
+ }
19535
+ </div>
19536
+ }
19537
+ }
19538
+ </div>
19539
+ }
19540
+ `, styles: [":host{display:flex;flex-direction:column;min-height:0;height:100%;font:var(--cad-font-size)/1.35 var(--cad-font);color:var(--cad-fg);background:var(--cad-bg)}.tb__board{flex:1;min-height:0;display:flex;gap:12px;padding:12px;overflow-x:auto;overflow-y:hidden;scrollbar-gutter:stable}.tb__col{flex:0 0 var(--cad-taskboard-col, 258px);display:flex;flex-direction:column;min-height:0;background:var(--cad-bg-subtle);border:1px solid var(--cad-border);border-radius:var(--cad-radius)}.tb__head{display:flex;align-items:center;gap:8px;padding:9px 11px;border-bottom:1px solid var(--cad-border)}.tb__dot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--_c, var(--cad-border-strong))}.tb__name{font-weight:500;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tb__count{font-family:var(--cad-font-mono);font-size:var(--cad-font-size-sm);padding:2px 7px;border-radius:var(--cad-radius-pill);background:var(--cad-elevated);color:var(--cad-fg-muted);font-variant-numeric:tabular-nums;white-space:nowrap}.tb__count.is-full{background:var(--cad-warning-soft);color:var(--cad-warning)}.tb__count.is-over{background:var(--cad-danger-soft);color:var(--cad-danger);font-weight:500}.tb__list{flex:1;min-height:0;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:8px;scrollbar-gutter:stable}.tb__list.cdk-drop-list-dragging{background:var(--cad-accent-soft)}.tb__card{background:var(--cad-surface);border:1px solid var(--cad-border);border-radius:var(--cad-radius-sm);padding:9px 10px;box-shadow:var(--cad-shadow-sm);display:flex;flex-direction:column;gap:7px;cursor:grab;-webkit-user-select:none;user-select:none;transition:border-color var(--cad-dur) var(--cad-ease),box-shadow var(--cad-dur) var(--cad-ease)}.tb__card:hover{border-color:var(--cad-border-strong);box-shadow:var(--cad-shadow-md)}.tb__card:focus-visible{outline:2px solid var(--cad-ring);outline-offset:1px}.tb__card.is-orphan{border-left:2px dashed var(--cad-warning)}:host(.is-static) .tb__card,:host(.is-static) .tb__row{cursor:default}.tb__title{font-weight:500;line-height:1.32}.tb__meta{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.tb__tag{font-family:var(--cad-font-mono);font-size:10px;padding:2px 6px;border-radius:var(--cad-radius-sm);background:var(--cad-elevated);color:var(--cad-fg-secondary)}.tb__due{font-family:var(--cad-font-mono);font-size:var(--cad-font-size-sm);color:var(--cad-fg-muted);font-variant-numeric:tabular-nums}.tb__due.is-late{color:var(--cad-danger);font-weight:500}.tb__sp{flex:1 1 auto}.tb__prog{height:3px;border-radius:var(--cad-radius-pill);background:var(--cad-elevated);overflow:hidden}.tb__prog i{display:block;height:100%;border-radius:var(--cad-radius-pill);background:var(--cad-accent)}.tb__prog.is-done i{background:var(--cad-success)}.tb__empty{padding:14px 10px;text-align:center;color:var(--cad-fg-muted);font-size:var(--cad-font-size-sm);border:1px dashed var(--cad-border-strong);border-radius:var(--cad-radius-sm)}.tb__rows{flex:1;min-height:0;overflow-y:auto;padding-bottom:12px;scrollbar-gutter:stable}.tb__grp{display:flex;align-items:center;gap:8px;width:100%;padding:8px 14px;background:var(--cad-bg-subtle);border-top:1px solid var(--cad-border);border-bottom:1px solid var(--cad-border);position:sticky;top:0;z-index:1;cursor:pointer;text-align:left;font:inherit;color:inherit}.tb__grp:hover{background:var(--cad-hover)}.tb__grp:focus-visible{outline:2px solid var(--cad-ring);outline-offset:-2px}.tb__grp cad-icon{color:var(--cad-fg-muted);transition:transform var(--cad-dur) var(--cad-ease)}.tb__grp.is-collapsed cad-icon{transform:rotate(-90deg)}.tb__grp b{font-weight:500}.tb__group-list{display:block}.tb__row{display:grid;grid-template-columns:1fr 92px 88px 26px;align-items:center;gap:12px;padding:8px 14px;border-bottom:1px solid var(--cad-border);cursor:grab;-webkit-user-select:none;user-select:none;background:var(--cad-bg)}.tb__row:hover{background:var(--cad-hover)}.tb__row:focus-visible{outline:2px solid var(--cad-ring);outline-offset:-2px}.tb__row.is-orphan{box-shadow:inset 2px 0 0 var(--cad-warning)}.tb__main{min-width:0;display:flex;flex-direction:column;gap:4px}.tb__main .tb__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tb__pct{display:flex;align-items:center;gap:7px}.tb__pct .tb__prog{flex:1}.tb__pct span{font-family:var(--cad-font-mono);font-size:10px;color:var(--cad-fg-muted);font-variant-numeric:tabular-nums}:host(.is-compact) .tb__row{grid-template-columns:1fr 26px}:host(.is-compact) .tb__cell-due,:host(.is-compact) .tb__pct{display:none}:host(.is-compact) .tb__mob{display:inline-flex}.tb__mob{display:none;align-items:center;gap:7px}.cdk-drag-preview{box-shadow:var(--cad-shadow-lg);border-radius:var(--cad-radius-sm);background:var(--cad-overlay);border:1px solid var(--cad-border)}.cdk-drag-placeholder{opacity:.34}.cdk-drag-animating{transition:transform .18s var(--cad-ease)}.tb__list.cdk-drop-list-dragging .tb__card:not(.cdk-drag-placeholder),.tb__group-list.cdk-drop-list-dragging .tb__row:not(.cdk-drag-placeholder){transition:transform .18s var(--cad-ease)}.tb__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}@media(prefers-reduced-motion:reduce){.cdk-drag-animating,.tb__list.cdk-drop-list-dragging .tb__card,.tb__grp cad-icon{transition:none}}\n"] }]
19541
+ }], propDecorators: { plan: [{ type: i0.Input, args: [{ isSignal: true, alias: "plan", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], dragDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragDisabled", required: false }] }], collapsedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedKeys", required: false }] }, { type: i0.Output, args: ["collapsedKeysChange"] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], today: [{ type: i0.Input, args: [{ isSignal: true, alias: "today", required: false }] }], taskMove: [{ type: i0.Output, args: ["taskMove"] }], taskAssign: [{ type: i0.Output, args: ["taskAssign"] }], taskOpen: [{ type: i0.Output, args: ["taskOpen"] }], wipExceeded: [{ type: i0.Output, args: ["wipExceeded"] }], cardTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => CadTaskCardDirective), { isSignal: true }] }], rowTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => CadTaskRowDirective), { isSignal: true }] }] } });
19542
+
17997
19543
  /**
17998
19544
  * Generated bundle index. Do not edit.
17999
19545
  */
18000
19546
 
18001
- export { BanGeocoder, CAD_BREAKPOINTS, CAD_CHART_PALETTE, CAD_COLOR_PRESETS, CAD_DEFAULT_BASEMAPS, CAD_DEFAULT_PRIORITY_BREAKPOINTS, CAD_DIALOG_DATA, CAD_FILTER_OPERATORS, CAD_GEOCODER, CAD_ICONS, CAD_ICONS_CEREMA, CAD_ICONS_CORE, CAD_ICONS_DATA, CAD_ICONS_FILES, CAD_ICONS_GEO, CAD_ICONS_SUBSET, CAD_ICONS_TOKEN, CAD_ICONS_UI, CAD_ICON_ALIASES, CAD_ICON_CATEGORIES, CAD_MAPLIBRE_CSS_URL, CAD_MAP_CHILD, CAD_MAP_DEFAULTS, CAD_MAP_GLOBAL_STYLES, CAD_MAP_GLYPHS, CAD_MAP_PALETTE, CAD_MASK_PRESETS, CAD_MOBILE_BREAKPOINT, CAD_SELECT_POSITIONS, CAD_SELECT_TRIGGER_STYLES, CAD_SHEET_DATA, CAD_TOUCH_DENSITY, CadAccordionComponent, CadAccordionPanelComponent, CadAvatarComponent, CadAvatarGroupComponent, CadBadgeComponent, CadBadgeDirective, CadBlockDirective, CadBlockOverlayComponent, CadBlockUiComponent, CadBottomNavComponent, CadBreadcrumbComponent, CadBreadcrumbItemDirective, CadBreakpointService, CadButtonComponent, CadButtonGroupComponent, CadCaptionDirective, CadCardComponent, CadCardDirective, CadCardHeaderDirective, CadCarouselComponent, CadCarouselItemDirective, CadCascadeSelectComponent, CadCellTemplateDirective, CadCenterComponent, CadChartComponent, CadCheckboxComponent, CadChipComponent, CadClusterComponent, CadColDirective, CadColorpickerComponent, CadColumnComponent, CadComboComponent, CadConfirmContentComponent, CadConfirmDialogComponent, CadConfirmService, CadContainerComponent, CadDataviewComponent, CadDataviewGridDirective, CadDataviewListDirective, CadDatepickerComponent, CadDialogComponent, CadDialogContainerComponent, CadDialogFooterDirective, CadDialogHeaderDirective, CadDialogRef, CadDialogService, CadDividerComponent, CadDrawerComponent, CadEditFocusDirective, CadEditorTemplateDirective, CadEmptyDirective, CadFieldComponent, CadFieldsetComponent, CadFieldsetLegendDirective, CadFilterChipComponent, CadFilterTemplateDirective, CadGeocodingService, CadGridComponent, CadGroupFooterDirective, CadGroupHeaderDirective, CadHeaderTemplateDirective, CadIconComponent, CadIconRegistry, CadIfDirective, CadImageComponent, CadInplaceComponent, CadInplaceContentDirective, CadInplaceDisplayDirective, CadInputDirective, CadJsonEditorComponent, CadKeyFilterDirective, CadListEmptyDirective, CadListFooterDirective, CadListHeaderDirective, CadListItemDirective, CadListboxComponent, CadMapBasemapRegistry, CadMapBasemapsComponent, CadMapCardComponent, CadMapChromeComponent, CadMapComponent, CadMapDrawEngine, CadMapLayerComponent, CadMapLayerGroupComponent, CadMapLayerToggleComponent, CadMapLegendComponent, CadMapMenuComponent, CadMapPanelComponent, CadMapPanels, CadMapRailComponent, CadMapRailItemComponent, CadMapSearchComponent, CadMapSectionComponent, CadMaskDirective, CadMenuComponent, CadMenubarComponent, CadMessageComponent, CadMeterComponent, CadMeterLabelDirective, CadMultiselectComponent, CadNavDrawerComponent, CadNavDrawerFooterDirective, CadNavDrawerHeaderDirective, CadNumberComponent, CadOptionListComponent, CadOrderlistComponent, CadOrgNodeDirective, CadOrgchartComponent, CadOverlayBase, CadPaginatorComponent, CadPanelComponent, CadPanelContentDirective, CadPanelHeaderDirective, CadPanelHeadingDirective, CadPasswordComponent, CadPicklistComponent, CadPopoverComponent, CadProgressComponent, CadRadioComponent, CadRadioGroupComponent, CadRatingComponent, CadRowActionsDirective, CadRowCountDirective, CadRowExpansionDirective, CadScrollTopComponent, CadSegmentedComponent, CadSelectBase, CadSelectComponent, CadSelectEmptyDirective, CadSelectFooterDirective, CadSelectHeaderDirective, CadSelectItemDirective, CadSelectSelectedDirective, CadSheetContainerComponent, CadSheetRef, CadSheetService, CadSidebarComponent, CadSidebarPanelDirective, CadSkeletonComponent, CadSliderComponent, CadSpacerComponent, CadSpeedDialComponent, CadSpinnerComponent, CadSplitButtonComponent, CadSplitComponent, CadSplitterComponent, CadSplitterPanelComponent, CadStackComponent, CadStepComponent, CadStepperComponent, CadSwitchComponent, CadTabComponent, CadTabContentDirective, CadTabLabelDirective, CadTableBase, CadTableComponent, CadTableEdit, CadTableEditUi, CadTableFooterDirective, CadTablePanelComponent, CadTableResponsive, CadTableSelection, CadTableSticky, CadTableVirtual, CadTabsComponent, CadTagComponent, CadTimelineComponent, CadTimelineContentDirective, CadTimelineMarkerDirective, CadTimelineOppositeDirective, CadToastComponent, CadToastItemDirective, CadToastService, CadToggleButtonComponent, CadTooltipComponent, CadTooltipDirective, CadTreeAdapter, CadTreeComponent, CadTreeNodeDirective, CadTreeselectComponent, CadUploadComponent, CadUploadContentDirective, CadUploadEmptyDirective, CadUploadItemDirective, CadValueAccessor, MAPLIBRE_MIN_CSS, addDays, addMonths, addYears, cadAggregate, cadApplyColumnOrder, cadApplyMask, cadArea, cadAutosizeWidth, cadBasemapThumbnail, cadBoundsOf, cadCategories, cadCentroid, cadColorExpr, cadCompare, cadCurrentOperator, cadDefaultMatchMode, cadDistance, cadDownloadCsv, cadFilterData, cadFilterKindOf, cadFilterOperators, cadFilterRows, cadFmtArea, cadFmtLength, cadFormatCell, cadFormatColor, cadFormatSize, cadGap, cadGeoJSONStats, cadGeometryKinds, cadHeaderMenuItems, cadHighlightJson, cadHslToRgb, cadHsvToRgb, cadId, cadIsGroupKey, cadJsonErrorPos, cadLegendFromStyle, cadLength, cadMapEstVivante, cadMapLayerOf, cadMapLayersOf, cadMatch, cadMoveColumn, cadMoveItems, cadNormalize, cadNormalizeOptions, cadNumberExpr, cadPadBounds, cadParseColor, cadParseResponsive, cadPropsTable, cadResolve, cadResolveResponsive, cadResponsive, cadRgbToHsl, cadRgbToHsv, cadRingArea, cadRowKey, cadRowPredicate, cadSameValue, cadSortData, cadStartResize, cadStyleToExpressions, cadUnmask, cadValidateGeoJSON, cadValueForOperator, cadWriteField, compareDay, daysInMonth, endOfMonth, ensureMaplibreCss, formatDate, isBetweenDay, isSameDay, isSameMonth, isValidDate, isoOf, isoWeek, loadChartJs, loadMaplibre, monthNames, pad2, parseIso, parseWithFormat, provideCadIcons, startOfDay, startOfMonth, toDate, weekdayNames };
19547
+ export { BanGeocoder, CAD_BREAKPOINTS, CAD_CHART_PALETTE, CAD_COLOR_PRESETS, CAD_DEFAULT_BASEMAPS, CAD_DEFAULT_PRIORITY_BREAKPOINTS, CAD_DIALOG_DATA, CAD_FILTER_OPERATORS, CAD_GEOCODER, CAD_ICONS, CAD_ICONS_CEREMA, CAD_ICONS_CORE, CAD_ICONS_DATA, CAD_ICONS_FILES, CAD_ICONS_GEO, CAD_ICONS_SUBSET, CAD_ICONS_TOKEN, CAD_ICONS_UI, CAD_ICON_ALIASES, CAD_ICON_CATEGORIES, CAD_MAPLIBRE_CSS_URL, CAD_MAP_CHILD, CAD_MAP_DEFAULTS, CAD_MAP_GLOBAL_STYLES, CAD_MAP_GLYPHS, CAD_MAP_PALETTE, CAD_MASK_PRESETS, CAD_MOBILE_BREAKPOINT, CAD_SELECT_POSITIONS, CAD_SELECT_TRIGGER_STYLES, CAD_SHEET_DATA, CAD_SHELL, CAD_TOUCH_DENSITY, CadAccordionComponent, CadAccordionPanelComponent, CadAppShellComponent, CadAvatarComponent, CadAvatarGroupComponent, CadBadgeComponent, CadBadgeDirective, CadBlockDirective, CadBlockOverlayComponent, CadBlockUiComponent, CadBottomNavComponent, CadBreadcrumbComponent, CadBreadcrumbItemDirective, CadBreakpointService, CadButtonComponent, CadButtonGroupComponent, CadCaptionDirective, CadCardComponent, CadCardDirective, CadCardHeaderDirective, CadCarouselComponent, CadCarouselItemDirective, CadCascadeSelectComponent, CadCellTemplateDirective, CadCenterComponent, CadChartComponent, CadCheckboxComponent, CadChipComponent, CadClusterComponent, CadColDirective, CadColorpickerComponent, CadColumnComponent, CadComboComponent, CadConfirmContentComponent, CadConfirmDialogComponent, CadConfirmService, CadContainerComponent, CadDataviewComponent, CadDataviewGridDirective, CadDataviewListDirective, CadDatepickerComponent, CadDialogComponent, CadDialogContainerComponent, CadDialogFooterDirective, CadDialogHeaderDirective, CadDialogRef, CadDialogService, CadDividerComponent, CadDrawerComponent, CadEditFocusDirective, CadEditorTemplateDirective, CadEmptyDirective, CadFieldComponent, CadFieldsetComponent, CadFieldsetLegendDirective, CadFilterChipComponent, CadFilterTemplateDirective, CadGeocodingService, CadGridComponent, CadGroupFooterDirective, CadGroupHeaderDirective, CadHeaderTemplateDirective, CadIconComponent, CadIconRegistry, CadIfDirective, CadImageComponent, CadInplaceComponent, CadInplaceContentDirective, CadInplaceDisplayDirective, CadInputDirective, CadJsonEditorComponent, CadKeyFilterDirective, CadListEmptyDirective, CadListFooterDirective, CadListHeaderDirective, CadListItemDirective, CadListboxComponent, CadMapBasemapRegistry, CadMapBasemapsComponent, CadMapCardComponent, CadMapChromeComponent, CadMapComponent, CadMapDrawEngine, CadMapLayerComponent, CadMapLayerGroupComponent, CadMapLayerToggleComponent, CadMapLegendComponent, CadMapMenuComponent, CadMapPanelComponent, CadMapPanels, CadMapRailComponent, CadMapRailItemComponent, CadMapSearchComponent, CadMapSectionComponent, CadMaskDirective, CadMenuComponent, CadMenubarComponent, CadMessageComponent, CadMeterComponent, CadMeterLabelDirective, CadMultiselectComponent, CadNavDrawerComponent, CadNavDrawerFooterDirective, CadNavDrawerHeaderDirective, CadNumberComponent, CadOptionListComponent, CadOrderlistComponent, CadOrgNodeDirective, CadOrgchartComponent, CadOverlayBase, CadPaginatorComponent, CadPanelComponent, CadPanelContentDirective, CadPanelHeaderDirective, CadPanelHeadingDirective, CadPasswordComponent, CadPicklistComponent, CadPopoverComponent, CadProgressComponent, CadRadioComponent, CadRadioGroupComponent, CadRatingComponent, CadRowActionsDirective, CadRowCountDirective, CadRowExpansionDirective, CadScrollTopComponent, CadSegmentedComponent, CadSelectBase, CadSelectComponent, CadSelectEmptyDirective, CadSelectFooterDirective, CadSelectHeaderDirective, CadSelectItemDirective, CadSelectSelectedDirective, CadSheetContainerComponent, CadSheetRef, CadSheetService, CadSidebarComponent, CadSidebarPanelDirective, CadSkeletonComponent, CadSliderComponent, CadSpacerComponent, CadSpeedDialComponent, CadSpinnerComponent, CadSplitButtonComponent, CadSplitComponent, CadSplitterComponent, CadSplitterPanelComponent, CadStackComponent, CadStepComponent, CadStepperComponent, CadSwitchComponent, CadTabComponent, CadTabContentDirective, CadTabLabelDirective, CadTableBase, CadTableComponent, CadTableEdit, CadTableEditUi, CadTableFooterDirective, CadTablePanelComponent, CadTableResponsive, CadTableSelection, CadTableSticky, CadTableVirtual, CadTabsComponent, CadTagComponent, CadTaskCardDirective, CadTaskRowDirective, CadTaskboardComponent, CadTimeAxis, CadTimelineComponent, CadTimelineContentDirective, CadTimelineMarkerDirective, CadTimelineOppositeDirective, CadToastComponent, CadToastItemDirective, CadToastService, CadToggleButtonComponent, CadTooltipComponent, CadTooltipDirective, CadTreeAdapter, CadTreeComponent, CadTreeNodeDirective, CadTreeselectComponent, CadUploadComponent, CadUploadContentDirective, CadUploadEmptyDirective, CadUploadItemDirective, CadValueAccessor, CadWorkCalendar, MAPLIBRE_MIN_CSS, addDays, addMonths, addYears, cadAggregate, cadApplyColumnOrder, cadApplyMask, cadArea, cadAutosizeWidth, cadBasemapThumbnail, cadBoundsOf, cadCategories, cadCentroid, cadColorExpr, cadCompare, cadCompareRank, cadCurrentOperator, cadDefaultMatchMode, cadDistance, cadDownloadCsv, cadDurationToMinutes, cadFilterData, cadFilterKindOf, cadFilterOperators, cadFilterRows, cadFindLinkCycle, cadFmtArea, cadFmtLength, cadFormatCell, cadFormatColor, cadFormatDuration, cadFormatSize, cadFrenchHolidays, cadGap, cadGeoJSONStats, cadGeometryKinds, cadHeaderMenuItems, cadHighlightJson, cadHslToRgb, cadHsvToRgb, cadId, cadIsGroupKey, cadIsRank, cadJsonErrorPos, cadLegendFromStyle, cadLength, cadMapEstVivante, cadMapLayerOf, cadMapLayersOf, cadMatch, cadMoveColumn, cadMoveItems, cadNormalize, cadNormalizeOptions, cadNormalizePlan, cadNumberExpr, cadPadBounds, cadParseColor, cadParseDuration, cadParseResponsive, cadPlanHeader, cadPlanTicks, cadPropsTable, cadRankBetween, cadRankFirst, cadRankLast, cadRankSequence, cadResolve, cadResolveResponsive, cadResponsive, cadRgbToHsl, cadRgbToHsv, cadRingArea, cadRowKey, cadRowPredicate, cadSameValue, cadScaleNext, cadScaleStart, cadSortData, cadStartResize, cadStyleToExpressions, cadUnmask, cadUnscheduled, cadValidateGeoJSON, cadValueForOperator, cadWriteField, compareDay, daysInMonth, endOfMonth, ensureMaplibreCss, formatDate, isBetweenDay, isSameDay, isSameMonth, isValidDate, isoOf, isoWeek, loadChartJs, loadMaplibre, monthNames, pad2, parseIso, parseWithFormat, provideCadIcons, startOfDay, startOfMonth, toDate, weekdayNames };
18002
19548
  //# sourceMappingURL=cadriciel-ui.mjs.map