@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.
- package/README.md +1 -1
- package/fesm2022/cadriciel-ui.mjs +1565 -19
- package/fesm2022/cadriciel-ui.mjs.map +1 -1
- package/package.json +2 -2
- package/types/cadriciel-ui.d.ts +663 -2
|
@@ -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: '
|
|
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 ? '
|
|
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' ? '
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[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
|