@forgeroute/ui 0.4.68 → 0.4.70

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.
@@ -7886,63 +7886,154 @@ i0.ɵɵngDeclareClassMetadata({
7886
7886
  }
7887
7887
  });
7888
7888
  const MIN_RESUME_MS = 1e3;
7889
+ const QUEUE_GAP_MS = 80;
7890
+ const COUNTDOWN_TICK_MS = 200;
7891
+ const DEFAULT_MAX_QUEUED = 20;
7889
7892
  var ToastService = class ToastService {
7890
7893
  constructor() {
7891
7894
  this.message = signal(null, ...ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []);
7892
7895
  this.type = signal("success", ...ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []);
7893
7896
  this.dismissible = signal(false, ...ngDevMode ? [{ debugName: "dismissible" }] : /* istanbul ignore next */ []);
7894
7897
  this.persistentLiveRegion = signal(false, ...ngDevMode ? [{ debugName: "persistentLiveRegion" }] : /* istanbul ignore next */ []);
7898
+ this.details = signal(null, ...ngDevMode ? [{ debugName: "details" }] : /* istanbul ignore next */ []);
7899
+ this.queuedCount = signal(0, ...ngDevMode ? [{ debugName: "queuedCount" }] : /* istanbul ignore next */ []);
7900
+ this.queueDefault = signal(false, ...ngDevMode ? [{ debugName: "queueDefault" }] : /* istanbul ignore next */ []);
7901
+ this.countdownDefault = signal(false, ...ngDevMode ? [{ debugName: "countdownDefault" }] : /* istanbul ignore next */ []);
7902
+ this.maxQueued = signal(DEFAULT_MAX_QUEUED, ...ngDevMode ? [{ debugName: "maxQueued" }] : /* istanbul ignore next */ []);
7903
+ this.remainingMs = signal(0, ...ngDevMode ? [{ debugName: "remainingMs" }] : /* istanbul ignore next */ []);
7904
+ this.totalMs = signal(0, ...ngDevMode ? [{ debugName: "totalMs" }] : /* istanbul ignore next */ []);
7905
+ this.action$ = new Subject();
7895
7906
  this._timer = null;
7907
+ this._ticker = null;
7908
+ this._advanceTimer = null;
7896
7909
  this._deadline = 0;
7897
7910
  this._remaining = 0;
7898
7911
  this._paused = false;
7912
+ this._queue = [];
7899
7913
  }
7900
- show(msg, duration = 3e3) {
7901
- this._display("success", msg, duration);
7914
+ show(msg, durationOrOptions = 3e3) {
7915
+ this._enter("success", msg, 3e3, durationOrOptions);
7902
7916
  }
7903
- info(msg, duration = 3e3) {
7904
- this._display("info", msg, duration);
7917
+ success(msg, durationOrOptions = 3e3) {
7918
+ this._enter("success", msg, 3e3, durationOrOptions);
7905
7919
  }
7906
- error(msg, duration = 4e3) {
7907
- this._display("error", msg, duration);
7920
+ info(msg, durationOrOptions = 3e3) {
7921
+ this._enter("info", msg, 3e3, durationOrOptions);
7922
+ }
7923
+ error(msg, durationOrOptions = 4e3) {
7924
+ this._enter("error", msg, 4e3, durationOrOptions);
7925
+ }
7926
+ notify(msg, options = {}) {
7927
+ const type = options.type ?? "success";
7928
+ this._enter(type, msg, type === "error" ? 4e3 : 3e3, options);
7908
7929
  }
7909
7930
  dismiss() {
7910
- this._clearTimer();
7911
- this._paused = false;
7912
- this.message.set(null);
7931
+ this._finish();
7932
+ }
7933
+ runAction(id) {
7934
+ const d = this.details();
7935
+ const a = d?.actions.find((x) => x.id === id);
7936
+ if (!d || !a || a.disabled) return;
7937
+ d.onAction?.(id);
7938
+ this.action$.next({
7939
+ id,
7940
+ key: d.key
7941
+ });
7942
+ if (!a.keepOpen) this._finish();
7913
7943
  }
7914
7944
  pause() {
7915
7945
  if (this._paused || !this._timer || this.message() === null) return;
7916
7946
  this._remaining = Math.max(0, this._deadline - Date.now());
7947
+ this.remainingMs.set(this._remaining);
7917
7948
  this._clearTimer();
7918
7949
  this._paused = true;
7919
7950
  }
7920
7951
  resume() {
7921
7952
  if (!this._paused) return;
7922
7953
  this._paused = false;
7923
- this._start(Math.max(this._remaining, MIN_RESUME_MS));
7954
+ this._start(Math.max(this._remaining, MIN_RESUME_MS), false);
7924
7955
  }
7925
7956
  ngOnDestroy() {
7926
7957
  this._clearTimer();
7958
+ this._clearTicker();
7959
+ this._clearAdvance();
7960
+ this._queue = [];
7927
7961
  this._paused = false;
7962
+ this.action$.complete();
7928
7963
  }
7929
7964
  _clearTimer() {
7930
7965
  if (this._timer) clearTimeout(this._timer);
7931
7966
  this._timer = null;
7932
7967
  }
7933
- _start(duration) {
7968
+ _clearTicker() {
7969
+ if (this._ticker) clearInterval(this._ticker);
7970
+ this._ticker = null;
7971
+ }
7972
+ _clearAdvance() {
7973
+ if (this._advanceTimer) clearTimeout(this._advanceTimer);
7974
+ this._advanceTimer = null;
7975
+ }
7976
+ _start(duration, fresh) {
7934
7977
  this._deadline = Date.now() + duration;
7935
- this._timer = setTimeout(() => {
7936
- this.message.set(null);
7937
- this._timer = null;
7938
- }, duration);
7978
+ this._timer = setTimeout(() => this._finish(), duration);
7979
+ if (fresh) this.totalMs.set(duration);
7980
+ this.remainingMs.set(duration);
7981
+ if (this.details()?.countdown && !this._ticker) this._ticker = setInterval(() => {
7982
+ if (!this._paused && this._timer) this.remainingMs.set(Math.max(0, this._deadline - Date.now()));
7983
+ }, COUNTDOWN_TICK_MS);
7984
+ }
7985
+ _finish() {
7986
+ this._clearTimer();
7987
+ this._clearTicker();
7988
+ this._paused = false;
7989
+ this.message.set(null);
7990
+ this.details.set(null);
7991
+ this.remainingMs.set(0);
7992
+ if (this._queue.length > 0 && !this._advanceTimer) this._advanceTimer = setTimeout(() => {
7993
+ this._advanceTimer = null;
7994
+ const next = this._queue.shift();
7995
+ this.queuedCount.set(this._queue.length);
7996
+ if (next) this._present(next);
7997
+ }, QUEUE_GAP_MS);
7998
+ }
7999
+ _enter(type, msg, defaultDuration, arg) {
8000
+ const options = typeof arg === "number" ? { duration: arg } : arg ?? {};
8001
+ const hasRich = !!(options.title || options.actions?.length);
8002
+ const countdown = options.countdown ?? this.countdownDefault();
8003
+ const details = hasRich || countdown || options.key !== void 0 || !!options.onAction ? {
8004
+ title: options.title,
8005
+ actions: options.actions ?? [],
8006
+ countdown,
8007
+ key: options.key,
8008
+ onAction: options.onAction
8009
+ } : null;
8010
+ const pending = {
8011
+ type,
8012
+ message: msg,
8013
+ duration: options.duration ?? defaultDuration,
8014
+ details
8015
+ };
8016
+ const queue = options.queue ?? this.queueDefault();
8017
+ const busy = this.message() !== null || this._advanceTimer !== null;
8018
+ if (queue && busy) {
8019
+ const key = options.key;
8020
+ if (key !== void 0 && (this.details()?.key === key || this._queue.some((q) => q.details?.key === key))) return;
8021
+ if (this._queue.length >= this.maxQueued()) return;
8022
+ this._queue.push(pending);
8023
+ this.queuedCount.set(this._queue.length);
8024
+ return;
8025
+ }
8026
+ this._clearAdvance();
8027
+ this._present(pending);
7939
8028
  }
7940
- _display(type, msg, duration) {
8029
+ _present(p) {
7941
8030
  this._clearTimer();
8031
+ this._clearTicker();
7942
8032
  this._paused = false;
7943
- this.type.set(type);
7944
- this.message.set(msg);
7945
- this._start(duration);
8033
+ this.type.set(p.type);
8034
+ this.details.set(p.details);
8035
+ this.message.set(p.message);
8036
+ this._start(p.duration, true);
7946
8037
  }
7947
8038
  static {
7948
8039
  this.ɵfac = i0.ɵɵngDeclareFactory({
@@ -8016,6 +8107,9 @@ i0.ɵɵngDeclareClassMetadata({
8016
8107
  }]
8017
8108
  });
8018
8109
  var ToastComponent = class ToastComponent {
8110
+ onAction(id) {
8111
+ this.toast.runAction(id);
8112
+ }
8019
8113
  onEscape(e) {
8020
8114
  e.stopPropagation();
8021
8115
  this.toast.dismiss();
@@ -8046,6 +8140,22 @@ var ToastComponent = class ToastComponent {
8046
8140
  this.showClose = computed(() => this.dismissible() ?? this.toast.dismissible(), ...ngDevMode ? [{ debugName: "showClose" }] : /* istanbul ignore next */ []);
8047
8141
  this.persistentLiveRegion = input(null, ...ngDevMode ? [{ debugName: "persistentLiveRegion" }] : /* istanbul ignore next */ []);
8048
8142
  this.persistent = computed(() => this.persistentLiveRegion() ?? this.toast.persistentLiveRegion(), ...ngDevMode ? [{ debugName: "persistent" }] : /* istanbul ignore next */ []);
8143
+ this.queue = input(null, ...ngDevMode ? [{ debugName: "queue" }] : /* istanbul ignore next */ []);
8144
+ this.countdown = input(null, ...ngDevMode ? [{ debugName: "countdown" }] : /* istanbul ignore next */ []);
8145
+ this.maxQueued = input(null, ...ngDevMode ? [{ debugName: "maxQueued" }] : /* istanbul ignore next */ []);
8146
+ this.countdownLabel = input("Dismissing in {n}s", ...ngDevMode ? [{ debugName: "countdownLabel" }] : /* istanbul ignore next */ []);
8147
+ this.queueLabel = input("+{n} more", ...ngDevMode ? [{ debugName: "queueLabel" }] : /* istanbul ignore next */ []);
8148
+ this.action = output();
8149
+ this.isRich = computed(() => {
8150
+ const d = this.toast.details();
8151
+ return !!d && (!!d.title || d.actions.length > 0 || d.countdown);
8152
+ }, ...ngDevMode ? [{ debugName: "isRich" }] : /* istanbul ignore next */ []);
8153
+ this.countdownPct = computed(() => {
8154
+ const total = this.toast.totalMs();
8155
+ return total > 0 ? Math.min(100, Math.max(0, this.toast.remainingMs() / total * 100)) : 0;
8156
+ }, ...ngDevMode ? [{ debugName: "countdownPct" }] : /* istanbul ignore next */ []);
8157
+ this.countdownText = computed(() => this.countdownLabel().replace("{n}", String(Math.ceil(this.toast.remainingMs() / 1e3))), ...ngDevMode ? [{ debugName: "countdownText" }] : /* istanbul ignore next */ []);
8158
+ this.queueText = computed(() => this.queueLabel().replace("{n}", String(this.toast.queuedCount())), ...ngDevMode ? [{ debugName: "queueText" }] : /* istanbul ignore next */ []);
8049
8159
  this._adoptedMessage = signal(null, ...ngDevMode ? [{ debugName: "_adoptedMessage" }] : /* istanbul ignore next */ []);
8050
8160
  this.adopted = computed(() => {
8051
8161
  const m = this._adoptedMessage();
@@ -8056,6 +8166,18 @@ var ToastComponent = class ToastComponent {
8056
8166
  this._focus = false;
8057
8167
  this._registry.register(this._id);
8058
8168
  inject(DestroyRef).onDestroy(() => this._registry.unregister(this._id));
8169
+ effect(() => {
8170
+ if (!this.isHost()) return;
8171
+ const q = this.queue(), c = this.countdown(), m = this.maxQueued();
8172
+ untracked(() => {
8173
+ if (q !== null) this.toast.queueDefault.set(q);
8174
+ if (c !== null) this.toast.countdownDefault.set(c);
8175
+ if (m !== null) this.toast.maxQueued.set(Math.max(0, Math.floor(m)));
8176
+ });
8177
+ });
8178
+ this.toast.action$.pipe(takeUntilDestroyed()).subscribe((e) => {
8179
+ if (this.isHost()) this.action.emit(e);
8180
+ });
8059
8181
  effect(() => {
8060
8182
  const host = this.isHost();
8061
8183
  const msg = this.toast.message();
@@ -8106,8 +8228,44 @@ var ToastComponent = class ToastComponent {
8106
8228
  isSignal: true,
8107
8229
  isRequired: false,
8108
8230
  transformFunction: null
8231
+ },
8232
+ queue: {
8233
+ classPropertyName: "queue",
8234
+ publicName: "queue",
8235
+ isSignal: true,
8236
+ isRequired: false,
8237
+ transformFunction: null
8238
+ },
8239
+ countdown: {
8240
+ classPropertyName: "countdown",
8241
+ publicName: "countdown",
8242
+ isSignal: true,
8243
+ isRequired: false,
8244
+ transformFunction: null
8245
+ },
8246
+ maxQueued: {
8247
+ classPropertyName: "maxQueued",
8248
+ publicName: "maxQueued",
8249
+ isSignal: true,
8250
+ isRequired: false,
8251
+ transformFunction: null
8252
+ },
8253
+ countdownLabel: {
8254
+ classPropertyName: "countdownLabel",
8255
+ publicName: "countdownLabel",
8256
+ isSignal: true,
8257
+ isRequired: false,
8258
+ transformFunction: null
8259
+ },
8260
+ queueLabel: {
8261
+ classPropertyName: "queueLabel",
8262
+ publicName: "queueLabel",
8263
+ isSignal: true,
8264
+ isRequired: false,
8265
+ transformFunction: null
8109
8266
  }
8110
8267
  },
8268
+ outputs: { action: "action" },
8111
8269
  ngImport: i0,
8112
8270
  template: `
8113
8271
  @if (isHost() && persistent()) {
@@ -8125,6 +8283,7 @@ var ToastComponent = class ToastComponent {
8125
8283
  <ng-template #card>
8126
8284
  <div
8127
8285
  class="t-toast"
8286
+ [class.t-rich]="isRich()"
8128
8287
  [class.t-error]="toast.type() === 'error'"
8129
8288
  [class.t-info]="toast.type() === 'info'"
8130
8289
  [class.t-adopted]="adopted()"
@@ -8146,7 +8305,14 @@ var ToastComponent = class ToastComponent {
8146
8305
  <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
8147
8306
  </svg>
8148
8307
  }
8149
- <span class="t-text">{{ toast.message() }}</span>
8308
+ @if (isRich()) {
8309
+ <span class="t-content">
8310
+ @if (toast.details()?.title; as title) { <span class="t-title" data-testid="toast-title">{{ title }}</span> }
8311
+ <span class="t-text">{{ toast.message() }}</span>
8312
+ </span>
8313
+ } @else {
8314
+ <span class="t-text">{{ toast.message() }}</span>
8315
+ }
8150
8316
  @if (showClose()) {
8151
8317
  <button type="button" class="t-close" aria-label="Dismiss notification" (click)="toast.dismiss()">
8152
8318
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
@@ -8154,11 +8320,31 @@ var ToastComponent = class ToastComponent {
8154
8320
  </svg>
8155
8321
  </button>
8156
8322
  }
8323
+ @if (toast.queuedCount() > 0) {
8324
+ <span class="t-queue" data-testid="toast-queue">{{ queueText() }}</span>
8325
+ }
8326
+ @if (toast.details(); as d) {
8327
+ @if (d.actions.length > 0) {
8328
+ <div class="t-actions" role="group" aria-label="Actions" data-testid="toast-actions">
8329
+ @for (a of d.actions; track a.id) {
8330
+ <button type="button" class="t-action"
8331
+ [class.t-action-primary]="a.variant === 'primary'" [class.t-action-danger]="a.variant === 'danger'"
8332
+ [disabled]="a.disabled" [attr.data-testid]="'toast-action-' + a.id" (click)="onAction(a.id)">{{ a.label }}</button>
8333
+ }
8334
+ </div>
8335
+ }
8336
+ @if (d.countdown) {
8337
+ <div class="t-countdown" aria-hidden="true">
8338
+ <div class="t-countdown-bar"><div class="t-countdown-fill" [style.width.%]="countdownPct()" data-testid="toast-countdown-fill"></div></div>
8339
+ <div class="t-countdown-label" data-testid="toast-countdown">{{ countdownText() }}</div>
8340
+ </div>
8341
+ }
8342
+ }
8157
8343
  </div>
8158
8344
  </ng-template>
8159
8345
  `,
8160
8346
  isInline: true,
8161
- styles: [".t-live{position:absolute;width:0;height:0;margin:0;padding:0;overflow:visible}.t-toast{position:fixed;bottom:max(24px,env(safe-area-inset-bottom,0px));left:50%;transform:translate(-50%);background:#047857;color:#fff;padding:11px 20px;border-radius:12px;font-size:.875rem;font-weight:500;z-index:9999;box-shadow:0 4px 16px #0000002e;animation:t-in .28s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px - env(safe-area-inset-left,0px) - env(safe-area-inset-right,0px));overflow-wrap:anywhere;word-break:break-word}.t-error{background:#dc2626}.t-info{background:#2563eb}.t-adopted{animation:none}.t-text{min-width:0}.t-close{position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -10px -4px 0;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.t-close:after{content:\"\";position:absolute;inset:-8px}.t-close:hover{background:#fff3}.t-close:focus-visible{outline:2px solid #fff;outline-offset:1px}@keyframes t-in{0%{opacity:0;transform:translate(-50%) translateY(10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){.t-toast{animation:none}}@media(max-width:480px){.t-toast{bottom:max(16px,env(safe-area-inset-bottom,16px));font-size:.8125rem;padding:10px 16px}}\n"],
8347
+ styles: [".t-live{position:absolute;width:0;height:0;margin:0;padding:0;overflow:visible}.t-toast{position:fixed;bottom:max(24px,env(safe-area-inset-bottom,0px));left:50%;transform:translate(-50%);background:#047857;color:#fff;padding:11px 20px;border-radius:12px;font-size:.875rem;font-weight:500;z-index:9999;box-shadow:0 4px 16px #0000002e;animation:t-in .28s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px - env(safe-area-inset-left,0px) - env(safe-area-inset-right,0px));overflow-wrap:anywhere;word-break:break-word}.t-error{background:#dc2626}.t-info{background:#2563eb}.t-adopted{animation:none}.t-text{min-width:0}.t-close{position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -10px -4px 0;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.t-close:after{content:\"\";position:absolute;inset:-8px}.t-close:hover{background:#fff3}.t-close:focus-visible{outline:2px solid #fff;outline-offset:1px}.t-rich{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:start;column-gap:8px;row-gap:10px;min-width:min(320px,calc(100vw - 32px))}.t-rich>svg:first-child{margin-top:2px}.t-content{display:flex;flex-direction:column;gap:2px;min-width:0}.t-title{font-weight:700}.t-queue{grid-column:1 / -1;justify-self:end;font-size:.75rem;font-weight:600;padding:1px 8px;border-radius:999px;background:#ffffff38}.t-actions{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:8px}.t-action{min-height:32px;padding:6px 14px;border-radius:8px;font:inherit;font-size:.8125rem;font-weight:600;cursor:pointer;color:#fff;background:#ffffff2e;border:1px solid rgba(255,255,255,.6)}.t-action:hover:not(:disabled){background:#ffffff4d}.t-action:focus-visible{outline:2px solid #fff;outline-offset:2px}.t-action:disabled{opacity:.5;cursor:not-allowed}.t-action-primary{background:#fff;color:#0f172a;border-color:#fff}.t-action-primary:hover:not(:disabled){background:#f1f5f9}.t-action-danger{background:#7f1d1d;border-color:#ffffffb3}.t-action-danger:hover:not(:disabled){background:#991b1b}.t-countdown{grid-column:1 / -1;display:flex;flex-direction:column;gap:4px}.t-countdown-bar{height:4px;border-radius:2px;background:#ffffff47;overflow:hidden}.t-countdown-fill{height:100%;background:#fff;transition:width .2s linear}.t-countdown-label{font-size:.75rem;opacity:.85;font-variant-numeric:tabular-nums}@media(prefers-reduced-motion:reduce){.t-countdown-fill{transition:none}}@media(pointer:coarse),(max-width:480px){.t-action{min-height:44px}}@keyframes t-in{0%{opacity:0;transform:translate(-50%) translateY(10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){.t-toast{animation:none}}@media(max-width:480px){.t-toast{bottom:max(16px,env(safe-area-inset-bottom,16px));font-size:.8125rem;padding:10px 16px}}\n"],
8162
8348
  dependencies: [{
8163
8349
  kind: "directive",
8164
8350
  type: NgTemplateOutlet,
@@ -8201,6 +8387,7 @@ i0.ɵɵngDeclareClassMetadata({
8201
8387
  <ng-template #card>
8202
8388
  <div
8203
8389
  class="t-toast"
8390
+ [class.t-rich]="isRich()"
8204
8391
  [class.t-error]="toast.type() === 'error'"
8205
8392
  [class.t-info]="toast.type() === 'info'"
8206
8393
  [class.t-adopted]="adopted()"
@@ -8222,7 +8409,14 @@ i0.ɵɵngDeclareClassMetadata({
8222
8409
  <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
8223
8410
  </svg>
8224
8411
  }
8225
- <span class="t-text">{{ toast.message() }}</span>
8412
+ @if (isRich()) {
8413
+ <span class="t-content">
8414
+ @if (toast.details()?.title; as title) { <span class="t-title" data-testid="toast-title">{{ title }}</span> }
8415
+ <span class="t-text">{{ toast.message() }}</span>
8416
+ </span>
8417
+ } @else {
8418
+ <span class="t-text">{{ toast.message() }}</span>
8419
+ }
8226
8420
  @if (showClose()) {
8227
8421
  <button type="button" class="t-close" aria-label="Dismiss notification" (click)="toast.dismiss()">
8228
8422
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
@@ -8230,10 +8424,30 @@ i0.ɵɵngDeclareClassMetadata({
8230
8424
  </svg>
8231
8425
  </button>
8232
8426
  }
8427
+ @if (toast.queuedCount() > 0) {
8428
+ <span class="t-queue" data-testid="toast-queue">{{ queueText() }}</span>
8429
+ }
8430
+ @if (toast.details(); as d) {
8431
+ @if (d.actions.length > 0) {
8432
+ <div class="t-actions" role="group" aria-label="Actions" data-testid="toast-actions">
8433
+ @for (a of d.actions; track a.id) {
8434
+ <button type="button" class="t-action"
8435
+ [class.t-action-primary]="a.variant === 'primary'" [class.t-action-danger]="a.variant === 'danger'"
8436
+ [disabled]="a.disabled" [attr.data-testid]="'toast-action-' + a.id" (click)="onAction(a.id)">{{ a.label }}</button>
8437
+ }
8438
+ </div>
8439
+ }
8440
+ @if (d.countdown) {
8441
+ <div class="t-countdown" aria-hidden="true">
8442
+ <div class="t-countdown-bar"><div class="t-countdown-fill" [style.width.%]="countdownPct()" data-testid="toast-countdown-fill"></div></div>
8443
+ <div class="t-countdown-label" data-testid="toast-countdown">{{ countdownText() }}</div>
8444
+ </div>
8445
+ }
8446
+ }
8233
8447
  </div>
8234
8448
  </ng-template>
8235
8449
  `,
8236
- styles: [".t-live{position:absolute;width:0;height:0;margin:0;padding:0;overflow:visible}.t-toast{position:fixed;bottom:max(24px,env(safe-area-inset-bottom,0px));left:50%;transform:translate(-50%);background:#047857;color:#fff;padding:11px 20px;border-radius:12px;font-size:.875rem;font-weight:500;z-index:9999;box-shadow:0 4px 16px #0000002e;animation:t-in .28s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px - env(safe-area-inset-left,0px) - env(safe-area-inset-right,0px));overflow-wrap:anywhere;word-break:break-word}.t-error{background:#dc2626}.t-info{background:#2563eb}.t-adopted{animation:none}.t-text{min-width:0}.t-close{position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -10px -4px 0;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.t-close:after{content:\"\";position:absolute;inset:-8px}.t-close:hover{background:#fff3}.t-close:focus-visible{outline:2px solid #fff;outline-offset:1px}@keyframes t-in{0%{opacity:0;transform:translate(-50%) translateY(10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){.t-toast{animation:none}}@media(max-width:480px){.t-toast{bottom:max(16px,env(safe-area-inset-bottom,16px));font-size:.8125rem;padding:10px 16px}}\n"]
8450
+ styles: [".t-live{position:absolute;width:0;height:0;margin:0;padding:0;overflow:visible}.t-toast{position:fixed;bottom:max(24px,env(safe-area-inset-bottom,0px));left:50%;transform:translate(-50%);background:#047857;color:#fff;padding:11px 20px;border-radius:12px;font-size:.875rem;font-weight:500;z-index:9999;box-shadow:0 4px 16px #0000002e;animation:t-in .28s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px - env(safe-area-inset-left,0px) - env(safe-area-inset-right,0px));overflow-wrap:anywhere;word-break:break-word}.t-error{background:#dc2626}.t-info{background:#2563eb}.t-adopted{animation:none}.t-text{min-width:0}.t-close{position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -10px -4px 0;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.t-close:after{content:\"\";position:absolute;inset:-8px}.t-close:hover{background:#fff3}.t-close:focus-visible{outline:2px solid #fff;outline-offset:1px}.t-rich{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:start;column-gap:8px;row-gap:10px;min-width:min(320px,calc(100vw - 32px))}.t-rich>svg:first-child{margin-top:2px}.t-content{display:flex;flex-direction:column;gap:2px;min-width:0}.t-title{font-weight:700}.t-queue{grid-column:1 / -1;justify-self:end;font-size:.75rem;font-weight:600;padding:1px 8px;border-radius:999px;background:#ffffff38}.t-actions{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:8px}.t-action{min-height:32px;padding:6px 14px;border-radius:8px;font:inherit;font-size:.8125rem;font-weight:600;cursor:pointer;color:#fff;background:#ffffff2e;border:1px solid rgba(255,255,255,.6)}.t-action:hover:not(:disabled){background:#ffffff4d}.t-action:focus-visible{outline:2px solid #fff;outline-offset:2px}.t-action:disabled{opacity:.5;cursor:not-allowed}.t-action-primary{background:#fff;color:#0f172a;border-color:#fff}.t-action-primary:hover:not(:disabled){background:#f1f5f9}.t-action-danger{background:#7f1d1d;border-color:#ffffffb3}.t-action-danger:hover:not(:disabled){background:#991b1b}.t-countdown{grid-column:1 / -1;display:flex;flex-direction:column;gap:4px}.t-countdown-bar{height:4px;border-radius:2px;background:#ffffff47;overflow:hidden}.t-countdown-fill{height:100%;background:#fff;transition:width .2s linear}.t-countdown-label{font-size:.75rem;opacity:.85;font-variant-numeric:tabular-nums}@media(prefers-reduced-motion:reduce){.t-countdown-fill{transition:none}}@media(pointer:coarse),(max-width:480px){.t-action{min-height:44px}}@keyframes t-in{0%{opacity:0;transform:translate(-50%) translateY(10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){.t-toast{animation:none}}@media(max-width:480px){.t-toast{bottom:max(16px,env(safe-area-inset-bottom,16px));font-size:.8125rem;padding:10px 16px}}\n"]
8237
8451
  }]
8238
8452
  }],
8239
8453
  ctorParameters: () => [],
@@ -8253,6 +8467,50 @@ i0.ɵɵngDeclareClassMetadata({
8253
8467
  alias: "persistentLiveRegion",
8254
8468
  required: false
8255
8469
  }]
8470
+ }],
8471
+ queue: [{
8472
+ type: i0.Input,
8473
+ args: [{
8474
+ isSignal: true,
8475
+ alias: "queue",
8476
+ required: false
8477
+ }]
8478
+ }],
8479
+ countdown: [{
8480
+ type: i0.Input,
8481
+ args: [{
8482
+ isSignal: true,
8483
+ alias: "countdown",
8484
+ required: false
8485
+ }]
8486
+ }],
8487
+ maxQueued: [{
8488
+ type: i0.Input,
8489
+ args: [{
8490
+ isSignal: true,
8491
+ alias: "maxQueued",
8492
+ required: false
8493
+ }]
8494
+ }],
8495
+ countdownLabel: [{
8496
+ type: i0.Input,
8497
+ args: [{
8498
+ isSignal: true,
8499
+ alias: "countdownLabel",
8500
+ required: false
8501
+ }]
8502
+ }],
8503
+ queueLabel: [{
8504
+ type: i0.Input,
8505
+ args: [{
8506
+ isSignal: true,
8507
+ alias: "queueLabel",
8508
+ required: false
8509
+ }]
8510
+ }],
8511
+ action: [{
8512
+ type: i0.Output,
8513
+ args: ["action"]
8256
8514
  }]
8257
8515
  }
8258
8516
  });
@@ -11260,7 +11518,7 @@ i0.ɵɵngDeclareClassMetadata({
11260
11518
  }
11261
11519
  });
11262
11520
  let reorderHelpSeq = 0;
11263
- function formatPhone$1(raw) {
11521
+ function formatPhone$2(raw) {
11264
11522
  if (!raw) return "—";
11265
11523
  const digits = raw.replace(/\D/g, "");
11266
11524
  if (digits.length === 10) return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
@@ -12629,7 +12887,7 @@ var DataTableComponent = class DataTableComponent {
12629
12887
  this._failedImages.update((s) => new Set(s).add(url));
12630
12888
  }
12631
12889
  phoneCell(value) {
12632
- return formatPhone$1(value);
12890
+ return formatPhone$2(value);
12633
12891
  }
12634
12892
  _safeHref(raw) {
12635
12893
  if (typeof raw !== "string") return null;
@@ -12718,7 +12976,7 @@ var DataTableComponent = class DataTableComponent {
12718
12976
  const v = item[col.key];
12719
12977
  return v ? this._datePipe.transform(v, col.dateFormat ?? "MMM d, y") ?? "—" : "—";
12720
12978
  }
12721
- case "phone": return formatPhone$1(item[col.key]);
12979
+ case "phone": return formatPhone$2(item[col.key]);
12722
12980
  case "link": return this.linkView(col, item).text;
12723
12981
  case "days-ago": {
12724
12982
  const days = item[col.daysKey ?? col.key];
@@ -13978,7 +14236,16 @@ var DataTableComponent = class DataTableComponent {
13978
14236
  kind: "component",
13979
14237
  type: ToastComponent,
13980
14238
  selector: "ui-toast",
13981
- inputs: ["dismissible", "persistentLiveRegion"]
14239
+ inputs: [
14240
+ "dismissible",
14241
+ "persistentLiveRegion",
14242
+ "queue",
14243
+ "countdown",
14244
+ "maxQueued",
14245
+ "countdownLabel",
14246
+ "queueLabel"
14247
+ ],
14248
+ outputs: ["action"]
13982
14249
  },
13983
14250
  {
13984
14251
  kind: "component",
@@ -20004,7 +20271,7 @@ i0.ɵɵngDeclareClassMetadata({
20004
20271
  }
20005
20272
  });
20006
20273
  let nextPickerId = 0;
20007
- function formatPhone(raw) {
20274
+ function formatPhone$1(raw) {
20008
20275
  if (!raw) return "";
20009
20276
  const normalized = raw.startsWith("+1") && raw.replace(/\D/g, "").length === 11 ? raw.slice(2) : raw;
20010
20277
  if (normalized.startsWith("+")) return normalized;
@@ -20019,7 +20286,7 @@ var ContactPickerComponent = class ContactPickerComponent {
20019
20286
  return `${this.uid}-opt-${i}`;
20020
20287
  }
20021
20288
  constructor() {
20022
- this.formatPhone = formatPhone;
20289
+ this.formatPhone = formatPhone$1;
20023
20290
  this.label = input("", ...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
20024
20291
  this.placeholder = input("Search by name...", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
20025
20292
  this.hint = input("", ...ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []);
@@ -22367,6 +22634,45 @@ i0.ɵɵngDeclareClassMetadata({
22367
22634
  }]
22368
22635
  }
22369
22636
  });
22637
+ const PASSWORD_MAX_LENGTH = 128;
22638
+ const BASE_RULES = [
22639
+ {
22640
+ id: "length",
22641
+ label: "At least 8 characters",
22642
+ test: (p) => p.length >= 8
22643
+ },
22644
+ {
22645
+ id: "upper",
22646
+ label: "One uppercase letter",
22647
+ test: (p) => /[A-Z]/.test(p)
22648
+ },
22649
+ {
22650
+ id: "lower",
22651
+ label: "One lowercase letter",
22652
+ test: (p) => /[a-z]/.test(p)
22653
+ },
22654
+ {
22655
+ id: "digit",
22656
+ label: "One digit (0-9)",
22657
+ test: (p) => /\d/.test(p)
22658
+ }
22659
+ ];
22660
+ const LISTED_SPECIAL_PASSWORD_RULES = [...BASE_RULES, {
22661
+ id: "special",
22662
+ label: "One special character (@ $ ! % * ? & #)",
22663
+ test: (p) => /[@$!%*?&#]/.test(p)
22664
+ }];
22665
+ const ASCII_SPECIAL_PASSWORD_RULES = [...BASE_RULES, {
22666
+ id: "special",
22667
+ label: "One special character (e.g. ! @ # $ %)",
22668
+ test: (p) => /[!-\/:-@[-`{-~]/.test(p)
22669
+ }];
22670
+ function createPasswordPolicyValidator(rules, maxLength = 128) {
22671
+ return (c) => {
22672
+ const p = String(c.value ?? "");
22673
+ return p.length <= maxLength && passwordMeetsRules(p, rules) ? null : { passwordRules: true };
22674
+ };
22675
+ }
22370
22676
  const KEYS = [
22371
22677
  "1",
22372
22678
  "2",
@@ -29513,6 +29819,9 @@ function provideForgerouteToast(options = {}) {
29513
29819
  return makeEnvironmentProviders([provideEnvironmentInitializer(() => {
29514
29820
  if (options.dismissible) inject(ToastService).dismissible.set(true);
29515
29821
  if (options.persistentLiveRegion) inject(ToastService).persistentLiveRegion.set(true);
29822
+ if (options.queue) inject(ToastService).queueDefault.set(true);
29823
+ if (options.countdown) inject(ToastService).countdownDefault.set(true);
29824
+ if (options.maxQueued !== void 0) inject(ToastService).maxQueued.set(Math.max(0, Math.floor(options.maxQueued)));
29516
29825
  if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
29517
29826
  const doc = inject(DOCUMENT);
29518
29827
  const appRef = inject(ApplicationRef);
@@ -32264,6 +32573,662 @@ const US_STATES = [
32264
32573
  label: "District of Columbia"
32265
32574
  }
32266
32575
  ];
32576
+ const US_LOCATIONS = {
32577
+ AL: [
32578
+ "Anniston",
32579
+ "Auburn",
32580
+ "Birmingham",
32581
+ "Decatur",
32582
+ "Dothan",
32583
+ "Florence",
32584
+ "Gadsden",
32585
+ "Hoover",
32586
+ "Huntsville",
32587
+ "Mobile",
32588
+ "Montgomery",
32589
+ "Tuscaloosa"
32590
+ ],
32591
+ AK: [
32592
+ "Anchorage",
32593
+ "Badger",
32594
+ "Fairbanks",
32595
+ "Juneau",
32596
+ "Ketchikan",
32597
+ "Sitka",
32598
+ "Wasilla"
32599
+ ],
32600
+ AZ: [
32601
+ "Avondale",
32602
+ "Buckeye",
32603
+ "Casa Grande",
32604
+ "Chandler",
32605
+ "Flagstaff",
32606
+ "Gilbert",
32607
+ "Glendale",
32608
+ "Goodyear",
32609
+ "Lake Havasu City",
32610
+ "Maricopa",
32611
+ "Mesa",
32612
+ "Peoria",
32613
+ "Phoenix",
32614
+ "Prescott",
32615
+ "Queen Creek",
32616
+ "Scottsdale",
32617
+ "Surprise",
32618
+ "Tempe",
32619
+ "Tucson",
32620
+ "Yuma"
32621
+ ],
32622
+ AR: [
32623
+ "Bentonville",
32624
+ "Conway",
32625
+ "Fayetteville",
32626
+ "Fort Smith",
32627
+ "Jonesboro",
32628
+ "Little Rock",
32629
+ "North Little Rock",
32630
+ "Pine Bluff",
32631
+ "Rogers",
32632
+ "Springdale"
32633
+ ],
32634
+ CA: [
32635
+ "Anaheim",
32636
+ "Antioch",
32637
+ "Bakersfield",
32638
+ "Berkeley",
32639
+ "Burbank",
32640
+ "Chula Vista",
32641
+ "Concord",
32642
+ "Corona",
32643
+ "El Monte",
32644
+ "Elk Grove",
32645
+ "Escondido",
32646
+ "Fontana",
32647
+ "Fremont",
32648
+ "Fresno",
32649
+ "Fullerton",
32650
+ "Garden Grove",
32651
+ "Glendale",
32652
+ "Hayward",
32653
+ "Huntington Beach",
32654
+ "Inglewood",
32655
+ "Irvine",
32656
+ "Lancaster",
32657
+ "Long Beach",
32658
+ "Los Angeles",
32659
+ "Modesto",
32660
+ "Moreno Valley",
32661
+ "Murrieta",
32662
+ "Ontario",
32663
+ "Oxnard",
32664
+ "Palmdale",
32665
+ "Pasadena",
32666
+ "Pomona",
32667
+ "Rancho Cucamonga",
32668
+ "Riverside",
32669
+ "Sacramento",
32670
+ "Salinas",
32671
+ "San Bernardino",
32672
+ "San Diego",
32673
+ "San Francisco",
32674
+ "San Jose",
32675
+ "Santa Ana",
32676
+ "Santa Clarita",
32677
+ "Santa Rosa",
32678
+ "Simi Valley",
32679
+ "Stockton",
32680
+ "Sunnyvale",
32681
+ "Thousand Oaks",
32682
+ "Torrance",
32683
+ "Vallejo",
32684
+ "Victorville",
32685
+ "Visalia"
32686
+ ],
32687
+ CO: [
32688
+ "Arvada",
32689
+ "Aurora",
32690
+ "Boulder",
32691
+ "Brighton",
32692
+ "Broomfield",
32693
+ "Castle Rock",
32694
+ "Colorado Springs",
32695
+ "Commerce City",
32696
+ "Denver",
32697
+ "Fort Collins",
32698
+ "Greeley",
32699
+ "Highlands Ranch",
32700
+ "Lakewood",
32701
+ "Longmont",
32702
+ "Loveland",
32703
+ "Parker",
32704
+ "Pueblo",
32705
+ "Thornton",
32706
+ "Westminster"
32707
+ ],
32708
+ CT: [
32709
+ "Bridgeport",
32710
+ "Bristol",
32711
+ "Danbury",
32712
+ "Greenwich",
32713
+ "Hartford",
32714
+ "Meriden",
32715
+ "New Britain",
32716
+ "New Haven",
32717
+ "Norwalk",
32718
+ "Stamford",
32719
+ "Waterbury"
32720
+ ],
32721
+ DE: [
32722
+ "Dover",
32723
+ "Middletown",
32724
+ "Newark",
32725
+ "Wilmington"
32726
+ ],
32727
+ FL: [
32728
+ "Boca Raton",
32729
+ "Bonita Springs",
32730
+ "Boynton Beach",
32731
+ "Bradenton",
32732
+ "Cape Coral",
32733
+ "Clearwater",
32734
+ "Coral Springs",
32735
+ "Davie",
32736
+ "Daytona Beach",
32737
+ "Deerfield Beach",
32738
+ "Deltona",
32739
+ "Fort Lauderdale",
32740
+ "Fort Myers",
32741
+ "Gainesville",
32742
+ "Hialeah",
32743
+ "Hollywood",
32744
+ "Homestead",
32745
+ "Jacksonville",
32746
+ "Jupiter",
32747
+ "Kissimmee",
32748
+ "Lakeland",
32749
+ "Lauderhill",
32750
+ "Margate",
32751
+ "Melbourne",
32752
+ "Miami",
32753
+ "Miami Gardens",
32754
+ "Miramar",
32755
+ "Naples",
32756
+ "Orlando",
32757
+ "Palm Bay",
32758
+ "Palm Beach Gardens",
32759
+ "Pembroke Pines",
32760
+ "Pensacola",
32761
+ "Pompano Beach",
32762
+ "Port St. Lucie",
32763
+ "St. Petersburg",
32764
+ "Sarasota",
32765
+ "Sunrise",
32766
+ "Tallahassee",
32767
+ "Tampa",
32768
+ "Temecula",
32769
+ "West Palm Beach"
32770
+ ],
32771
+ GA: [
32772
+ "Albany",
32773
+ "Alpharetta",
32774
+ "Athens",
32775
+ "Atlanta",
32776
+ "Augusta",
32777
+ "Columbus",
32778
+ "Gainesville",
32779
+ "Johns Creek",
32780
+ "Macon",
32781
+ "Marietta",
32782
+ "Roswell",
32783
+ "Sandy Springs",
32784
+ "Savannah",
32785
+ "South Fulton",
32786
+ "Valdosta",
32787
+ "Warner Robins"
32788
+ ],
32789
+ HI: [
32790
+ "Hilo",
32791
+ "Honolulu",
32792
+ "Kailua",
32793
+ "Kaneohe",
32794
+ "Mililani Town",
32795
+ "Pearl City"
32796
+ ],
32797
+ ID: [
32798
+ "Boise",
32799
+ "Caldwell",
32800
+ "Idaho Falls",
32801
+ "Meridian",
32802
+ "Nampa",
32803
+ "Pocatello",
32804
+ "Twin Falls"
32805
+ ],
32806
+ IL: [
32807
+ "Aurora",
32808
+ "Bloomington",
32809
+ "Champaign",
32810
+ "Chicago",
32811
+ "Cicero",
32812
+ "Elgin",
32813
+ "Evanston",
32814
+ "Joliet",
32815
+ "Naperville",
32816
+ "Peoria",
32817
+ "Rockford",
32818
+ "Springfield",
32819
+ "Waukegan"
32820
+ ],
32821
+ IN: [
32822
+ "Anderson",
32823
+ "Bloomington",
32824
+ "Carmel",
32825
+ "Evansville",
32826
+ "Fishers",
32827
+ "Fort Wayne",
32828
+ "Hammond",
32829
+ "Indianapolis",
32830
+ "Muncie",
32831
+ "South Bend",
32832
+ "Terre Haute"
32833
+ ],
32834
+ IA: [
32835
+ "Ames",
32836
+ "Cedar Rapids",
32837
+ "Council Bluffs",
32838
+ "Davenport",
32839
+ "Des Moines",
32840
+ "Dubuque",
32841
+ "Iowa City",
32842
+ "Sioux City",
32843
+ "Waterloo"
32844
+ ],
32845
+ KS: [
32846
+ "Kansas City",
32847
+ "Lawrence",
32848
+ "Olathe",
32849
+ "Overland Park",
32850
+ "Salina",
32851
+ "Topeka",
32852
+ "Wichita"
32853
+ ],
32854
+ KY: [
32855
+ "Bowling Green",
32856
+ "Covington",
32857
+ "Frankfort",
32858
+ "Lexington",
32859
+ "Louisville",
32860
+ "Owensboro",
32861
+ "Richmond"
32862
+ ],
32863
+ LA: [
32864
+ "Baton Rouge",
32865
+ "Bossier City",
32866
+ "Kenner",
32867
+ "Lafayette",
32868
+ "Lake Charles",
32869
+ "Metairie",
32870
+ "Monroe",
32871
+ "New Orleans",
32872
+ "Shreveport"
32873
+ ],
32874
+ ME: [
32875
+ "Auburn",
32876
+ "Augusta",
32877
+ "Bangor",
32878
+ "Lewiston",
32879
+ "Portland",
32880
+ "South Portland"
32881
+ ],
32882
+ MD: [
32883
+ "Annapolis",
32884
+ "Baltimore",
32885
+ "Bowie",
32886
+ "Columbia",
32887
+ "Frederick",
32888
+ "Gaithersburg",
32889
+ "Germantown",
32890
+ "Hagerstown",
32891
+ "Rockville",
32892
+ "Silver Spring",
32893
+ "Waldorf"
32894
+ ],
32895
+ MA: [
32896
+ "Boston",
32897
+ "Brockton",
32898
+ "Cambridge",
32899
+ "Fall River",
32900
+ "Lowell",
32901
+ "Lynn",
32902
+ "New Bedford",
32903
+ "Newton",
32904
+ "Quincy",
32905
+ "Springfield",
32906
+ "Worcester"
32907
+ ],
32908
+ MI: [
32909
+ "Ann Arbor",
32910
+ "Dearborn",
32911
+ "Detroit",
32912
+ "Flint",
32913
+ "Grand Rapids",
32914
+ "Kalamazoo",
32915
+ "Lansing",
32916
+ "Livonia",
32917
+ "Sterling Heights",
32918
+ "Warren"
32919
+ ],
32920
+ MN: [
32921
+ "Bloomington",
32922
+ "Brooklyn Park",
32923
+ "Burnsville",
32924
+ "Duluth",
32925
+ "Eden Prairie",
32926
+ "Minneapolis",
32927
+ "Plymouth",
32928
+ "Rochester",
32929
+ "St. Cloud",
32930
+ "St. Paul"
32931
+ ],
32932
+ MS: [
32933
+ "Biloxi",
32934
+ "Gulfport",
32935
+ "Hattiesburg",
32936
+ "Jackson",
32937
+ "Meridian",
32938
+ "Southaven",
32939
+ "Tupelo"
32940
+ ],
32941
+ MO: [
32942
+ "Columbia",
32943
+ "Independence",
32944
+ "Kansas City",
32945
+ "Lee's Summit",
32946
+ "O'Fallon",
32947
+ "Springfield",
32948
+ "St. Joseph",
32949
+ "St. Louis"
32950
+ ],
32951
+ MT: [
32952
+ "Billings",
32953
+ "Bozeman",
32954
+ "Butte",
32955
+ "Great Falls",
32956
+ "Helena",
32957
+ "Missoula"
32958
+ ],
32959
+ NE: [
32960
+ "Bellevue",
32961
+ "Grand Island",
32962
+ "Kearney",
32963
+ "Lincoln",
32964
+ "Omaha"
32965
+ ],
32966
+ NV: [
32967
+ "Carson City",
32968
+ "Henderson",
32969
+ "Las Vegas",
32970
+ "North Las Vegas",
32971
+ "Reno",
32972
+ "Sparks",
32973
+ "Sunrise Manor"
32974
+ ],
32975
+ NH: [
32976
+ "Concord",
32977
+ "Dover",
32978
+ "Manchester",
32979
+ "Nashua",
32980
+ "Portsmouth"
32981
+ ],
32982
+ NJ: [
32983
+ "Camden",
32984
+ "Edison",
32985
+ "Elizabeth",
32986
+ "Jersey City",
32987
+ "Newark",
32988
+ "Paterson",
32989
+ "Toms River",
32990
+ "Trenton"
32991
+ ],
32992
+ NM: [
32993
+ "Albuquerque",
32994
+ "Las Cruces",
32995
+ "Rio Rancho",
32996
+ "Roswell",
32997
+ "Santa Fe"
32998
+ ],
32999
+ NY: [
33000
+ "Albany",
33001
+ "Amherst",
33002
+ "Bronx",
33003
+ "Brooklyn",
33004
+ "Buffalo",
33005
+ "Cheektowaga",
33006
+ "Manhattan",
33007
+ "New Rochelle",
33008
+ "New York",
33009
+ "Niagara Falls",
33010
+ "Queens",
33011
+ "Rochester",
33012
+ "Staten Island",
33013
+ "Syracuse",
33014
+ "Utica",
33015
+ "Yonkers"
33016
+ ],
33017
+ NC: [
33018
+ "Asheville",
33019
+ "Cary",
33020
+ "Chapel Hill",
33021
+ "Charlotte",
33022
+ "Concord",
33023
+ "Durham",
33024
+ "Fayetteville",
33025
+ "Gastonia",
33026
+ "Greensboro",
33027
+ "High Point",
33028
+ "Raleigh",
33029
+ "Wilmington",
33030
+ "Winston-Salem"
33031
+ ],
33032
+ ND: [
33033
+ "Bismarck",
33034
+ "Fargo",
33035
+ "Grand Forks",
33036
+ "Minot"
33037
+ ],
33038
+ OH: [
33039
+ "Akron",
33040
+ "Canton",
33041
+ "Cincinnati",
33042
+ "Cleveland",
33043
+ "Columbus",
33044
+ "Dayton",
33045
+ "Lorain",
33046
+ "Parma",
33047
+ "Toledo",
33048
+ "Youngstown"
33049
+ ],
33050
+ OK: [
33051
+ "Broken Arrow",
33052
+ "Edmond",
33053
+ "Lawton",
33054
+ "Moore",
33055
+ "Norman",
33056
+ "Oklahoma City",
33057
+ "Tulsa"
33058
+ ],
33059
+ OR: [
33060
+ "Beaverton",
33061
+ "Bend",
33062
+ "Eugene",
33063
+ "Gresham",
33064
+ "Hillsboro",
33065
+ "Medford",
33066
+ "Portland",
33067
+ "Salem"
33068
+ ],
33069
+ PA: [
33070
+ "Allentown",
33071
+ "Bethlehem",
33072
+ "Erie",
33073
+ "Philadelphia",
33074
+ "Pittsburgh",
33075
+ "Reading",
33076
+ "Scranton"
33077
+ ],
33078
+ RI: [
33079
+ "Cranston",
33080
+ "Pawtucket",
33081
+ "Providence",
33082
+ "Warwick",
33083
+ "Woonsocket"
33084
+ ],
33085
+ SC: [
33086
+ "Charleston",
33087
+ "Columbia",
33088
+ "Florence",
33089
+ "Greenville",
33090
+ "Mount Pleasant",
33091
+ "North Charleston",
33092
+ "Rock Hill",
33093
+ "Spartanburg"
33094
+ ],
33095
+ SD: [
33096
+ "Aberdeen",
33097
+ "Rapid City",
33098
+ "Sioux Falls"
33099
+ ],
33100
+ TN: [
33101
+ "Chattanooga",
33102
+ "Clarksville",
33103
+ "Jackson",
33104
+ "Johnson City",
33105
+ "Kingsport",
33106
+ "Knoxville",
33107
+ "Memphis",
33108
+ "Murfreesboro",
33109
+ "Nashville"
33110
+ ],
33111
+ TX: [
33112
+ "Abilene",
33113
+ "Allen",
33114
+ "Amarillo",
33115
+ "Arlington",
33116
+ "Austin",
33117
+ "Beaumont",
33118
+ "Brownsville",
33119
+ "Carrollton",
33120
+ "Cedar Park",
33121
+ "College Station",
33122
+ "Corpus Christi",
33123
+ "Dallas",
33124
+ "Denton",
33125
+ "El Paso",
33126
+ "Fort Worth",
33127
+ "Frisco",
33128
+ "Garland",
33129
+ "Georgetown",
33130
+ "Grand Prairie",
33131
+ "Houston",
33132
+ "Irving",
33133
+ "Killeen",
33134
+ "Laredo",
33135
+ "League City",
33136
+ "Lewisville",
33137
+ "Lubbock",
33138
+ "Mcallen",
33139
+ "McKinney",
33140
+ "Mesquite",
33141
+ "Midland",
33142
+ "Missouri City",
33143
+ "New Braunfels",
33144
+ "Odessa",
33145
+ "Pasadena",
33146
+ "Pearland",
33147
+ "Plano",
33148
+ "Richardson",
33149
+ "Round Rock",
33150
+ "San Antonio",
33151
+ "Sugar Land",
33152
+ "Tyler",
33153
+ "Waco",
33154
+ "Wichita Falls"
33155
+ ],
33156
+ UT: [
33157
+ "Ogden",
33158
+ "Orem",
33159
+ "Provo",
33160
+ "Salt Lake City",
33161
+ "Sandy",
33162
+ "St. George",
33163
+ "West Jordan",
33164
+ "West Valley City"
33165
+ ],
33166
+ VT: [
33167
+ "Burlington",
33168
+ "Essex Junction",
33169
+ "Montpelier",
33170
+ "Rutland"
33171
+ ],
33172
+ VA: [
33173
+ "Alexandria",
33174
+ "Arlington",
33175
+ "Chesapeake",
33176
+ "Hampton",
33177
+ "Newport News",
33178
+ "Norfolk",
33179
+ "Portsmouth",
33180
+ "Richmond",
33181
+ "Roanoke",
33182
+ "Virginia Beach"
33183
+ ],
33184
+ WA: [
33185
+ "Bellevue",
33186
+ "Bellingham",
33187
+ "Everett",
33188
+ "Kent",
33189
+ "Kirkland",
33190
+ "Renton",
33191
+ "Seattle",
33192
+ "Spokane",
33193
+ "Tacoma",
33194
+ "Vancouver"
33195
+ ],
33196
+ WV: [
33197
+ "Charleston",
33198
+ "Huntington",
33199
+ "Morgantown",
33200
+ "Parkersburg"
33201
+ ],
33202
+ WI: [
33203
+ "Appleton",
33204
+ "Green Bay",
33205
+ "Kenosha",
33206
+ "Madison",
33207
+ "Milwaukee",
33208
+ "Oshkosh",
33209
+ "Racine"
33210
+ ],
33211
+ WY: [
33212
+ "Casper",
33213
+ "Cheyenne",
33214
+ "Gillette",
33215
+ "Laramie"
33216
+ ]
33217
+ };
33218
+ const COMMON_DETAIL_GROUP_STYLES = {
33219
+ activity: {
33220
+ title: "Activity",
33221
+ icon: "message",
33222
+ background: "#eff6ff"
33223
+ },
33224
+ notes: {
33225
+ title: "Notes",
33226
+ icon: "add-note",
33227
+ background: "#f1f5f9"
33228
+ }
33229
+ };
33230
+ const DETAIL_CARD_HEADER_BG = "#f8fafc";
33231
+ const DETAIL_GROUP_HEADER_BG = "#eaf3ff";
32267
33232
  var ShellProjectedStylesComponent = class ShellProjectedStylesComponent {
32268
33233
  static {
32269
33234
  this.ɵfac = i0.ɵɵngDeclareFactory({
@@ -32889,7 +33854,16 @@ var AppShellComponent = class AppShellComponent {
32889
33854
  kind: "component",
32890
33855
  type: ToastComponent,
32891
33856
  selector: "ui-toast",
32892
- inputs: ["dismissible", "persistentLiveRegion"]
33857
+ inputs: [
33858
+ "dismissible",
33859
+ "persistentLiveRegion",
33860
+ "queue",
33861
+ "countdown",
33862
+ "maxQueued",
33863
+ "countdownLabel",
33864
+ "queueLabel"
33865
+ ],
33866
+ outputs: ["action"]
32893
33867
  },
32894
33868
  {
32895
33869
  kind: "component",
@@ -41724,6 +42698,44 @@ function layoutLanes(boxes) {
41724
42698
  flush();
41725
42699
  return result;
41726
42700
  }
42701
+ function periodGroup(todayStr) {
42702
+ if (!isValidDateStr(todayStr)) return () => null;
42703
+ const today = fromDateStr(todayStr);
42704
+ const tomorrow = toDateStr(new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1));
42705
+ const weekEnd = toDateStr(new Date(today.getFullYear(), today.getMonth(), today.getDate() + (6 - today.getDay())));
42706
+ return (dateStr) => {
42707
+ if (!isValidDateStr(dateStr)) return null;
42708
+ if (dateStr < todayStr) return {
42709
+ key: "overdue",
42710
+ label: "Overdue"
42711
+ };
42712
+ if (dateStr === todayStr) return {
42713
+ key: "today",
42714
+ label: "Today"
42715
+ };
42716
+ if (dateStr === tomorrow) return {
42717
+ key: "tomorrow",
42718
+ label: "Tomorrow"
42719
+ };
42720
+ if (dateStr <= weekEnd) return {
42721
+ key: "this-week",
42722
+ label: "This Week"
42723
+ };
42724
+ return {
42725
+ key: "later",
42726
+ label: "Later"
42727
+ };
42728
+ };
42729
+ }
42730
+ const DAY_LEVEL_TEXT = {
42731
+ low: "low activity",
42732
+ medium: "medium activity",
42733
+ high: "high activity",
42734
+ critical: "critical activity"
42735
+ };
42736
+ function isDayLevel(level) {
42737
+ return level === "low" || level === "medium" || level === "high" || level === "critical";
42738
+ }
41727
42739
  const PENDING_FOCUS_MS = 1e3;
41728
42740
  var CalendarComponent = class CalendarComponent {
41729
42741
  clickable(dateStr) {
@@ -41758,11 +42770,15 @@ var CalendarComponent = class CalendarComponent {
41758
42770
  this.endHour = input(21, ...ngDevMode ? [{ debugName: "endHour" }] : /* istanbul ignore next */ []);
41759
42771
  this.emptyMessage = input("No events in this period.", ...ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []);
41760
42772
  this.dayLabelFn = input(null, ...ngDevMode ? [{ debugName: "dayLabelFn" }] : /* istanbul ignore next */ []);
42773
+ this.dayDecorationFn = input(null, ...ngDevMode ? [{ debugName: "dayDecorationFn" }] : /* istanbul ignore next */ []);
42774
+ this.itemActionsFn = input(null, ...ngDevMode ? [{ debugName: "itemActionsFn" }] : /* istanbul ignore next */ []);
42775
+ this.agendaGroupFn = input(null, ...ngDevMode ? [{ debugName: "agendaGroupFn" }] : /* istanbul ignore next */ []);
41761
42776
  this.rangeChange = output();
41762
42777
  this.itemClick = output();
41763
42778
  this.dateClick = output();
41764
42779
  this.slotClick = output();
41765
42780
  this.moreClick = output();
42781
+ this.itemAction = output();
41766
42782
  this.host = inject(ElementRef);
41767
42783
  this.weekDayLabels = WEEK_DAY_LABELS;
41768
42784
  this.weekDayNames = [
@@ -41854,13 +42870,16 @@ var CalendarComponent = class CalendarComponent {
41854
42870
  const max = this.maxVisible();
41855
42871
  const cells = this.monthCells().map((day) => {
41856
42872
  const items = this.byDate()[day.dateStr] ?? [];
42873
+ const deco = this.decoration(day.dateStr, items);
41857
42874
  return {
41858
42875
  day,
41859
42876
  items,
41860
42877
  visible: items.slice(0, max),
41861
42878
  overflow: Math.max(0, items.length - max),
41862
42879
  clickable: this.clickable(day.dateStr),
41863
- label: this.dayAriaLabel(day.dateStr, items)
42880
+ label: this.dayAriaLabel(day.dateStr, items, deco.level),
42881
+ level: deco.level,
42882
+ tooltip: deco.tooltip
41864
42883
  };
41865
42884
  });
41866
42885
  return Array.from({ length: 6 }, (_, r) => ({
@@ -41881,10 +42900,13 @@ var CalendarComponent = class CalendarComponent {
41881
42900
  top: b.top,
41882
42901
  height: b.height
41883
42902
  })));
42903
+ const deco = this.decoration(day.dateStr, items);
41884
42904
  return {
41885
42905
  day,
41886
42906
  label: formatDayLabel(day.dateStr),
41887
42907
  clickable: this.clickable(day.dateStr),
42908
+ level: deco.level,
42909
+ tooltip: deco.tooltip,
41888
42910
  allDay: items.filter((e) => e.allDay || !e.startTime),
41889
42911
  timed: boxes.map((b) => ({
41890
42912
  ...b,
@@ -41901,6 +42923,41 @@ var CalendarComponent = class CalendarComponent {
41901
42923
  }, ...ngDevMode ? [{ debugName: "focusDate" }] : /* istanbul ignore next */ []);
41902
42924
  this.pendingFocus = null;
41903
42925
  this.pendingSawLoading = false;
42926
+ this.actionsById = computed(() => {
42927
+ const map = /* @__PURE__ */ new Map();
42928
+ const fn = this.itemActionsFn();
42929
+ if (!fn) return map;
42930
+ for (const e of this.items()) {
42931
+ const acts = fn(e);
42932
+ const seen = /* @__PURE__ */ new Set();
42933
+ const valid = Array.isArray(acts) ? acts.filter((a) => {
42934
+ if (!a || !a.id || !a.label || seen.has(a.id)) return false;
42935
+ seen.add(a.id);
42936
+ return true;
42937
+ }) : [];
42938
+ if (valid.length) map.set(e.id, valid);
42939
+ }
42940
+ return map;
42941
+ }, ...ngDevMode ? [{ debugName: "actionsById" }] : /* istanbul ignore next */ []);
42942
+ this.uid = `ui-cal-${CalendarComponent.nextUid++}`;
42943
+ this.agendaSections = computed(() => {
42944
+ const fn = this.agendaGroupFn();
42945
+ if (!fn) return null;
42946
+ const sections = [];
42947
+ for (const g of this.agendaGroups()) {
42948
+ const grp = fn(g.dateStr);
42949
+ const key = grp?.key ?? "";
42950
+ const last = sections.at(-1);
42951
+ if (last && last.key === key) last.groups.push(g);
42952
+ else sections.push({
42953
+ key,
42954
+ label: grp?.label ?? "",
42955
+ id: `${this.uid}-agenda-${sections.length}`,
42956
+ groups: [g]
42957
+ });
42958
+ }
42959
+ return sections;
42960
+ }, ...ngDevMode ? [{ debugName: "agendaSections" }] : /* istanbul ignore next */ []);
41904
42961
  this.host.nativeElement.addEventListener("pointerdown", () => {
41905
42962
  this.pendingFocus = null;
41906
42963
  });
@@ -42007,11 +43064,46 @@ var CalendarComponent = class CalendarComponent {
42007
43064
  agendaMeta(e) {
42008
43065
  return [e.timeLabel ?? (!e.allDay && e.startTime ? this.timeRange(e) : ""), ...e.meta ?? []].filter(Boolean);
42009
43066
  }
42010
- dayAriaLabel(dateStr, items) {
43067
+ dayAriaLabel(dateStr, items, level = null) {
42011
43068
  const fn = this.dayLabelFn();
42012
43069
  if (fn) return fn(dateStr, items);
42013
43070
  const label = formatDayLabel(dateStr);
42014
- return items.length ? `${label}, ${items.length} ${items.length === 1 ? "event" : "events"}` : label;
43071
+ const base = items.length ? `${label}, ${items.length} ${items.length === 1 ? "event" : "events"}` : label;
43072
+ return level ? `${base}, ${DAY_LEVEL_TEXT[level]}` : base;
43073
+ }
43074
+ decoration(dateStr, items) {
43075
+ const d = this.dayDecorationFn()?.(dateStr, items);
43076
+ return {
43077
+ level: d && isDayLevel(d.level) ? d.level : null,
43078
+ tooltip: d?.tooltip ?? ""
43079
+ };
43080
+ }
43081
+ levelText(level) {
43082
+ return DAY_LEVEL_TEXT[level];
43083
+ }
43084
+ static {
43085
+ this.NO_ACTIONS = [];
43086
+ }
43087
+ actionsFor(item) {
43088
+ return this.actionsById().get(item.id) ?? CalendarComponent.NO_ACTIONS;
43089
+ }
43090
+ actionIconD(a) {
43091
+ return a.icon ? iconPath(a.icon) ?? null : null;
43092
+ }
43093
+ onActionClick(item, action, event) {
43094
+ event.stopPropagation();
43095
+ if (action.disabled) return;
43096
+ this.itemAction.emit({
43097
+ item,
43098
+ actionId: action.id
43099
+ });
43100
+ }
43101
+ onActionsEscape(event) {
43102
+ event.stopPropagation();
43103
+ event.currentTarget.parentElement?.querySelector(":scope > .event-chip, :scope > .allday-chip, :scope > .timed-event, :scope > .agenda-event")?.focus();
43104
+ }
43105
+ static {
43106
+ this.nextUid = 0;
42015
43107
  }
42016
43108
  onDateClick(dateStr) {
42017
43109
  if (this.clickable(dateStr)) this.dateClick.emit(dateStr);
@@ -42256,6 +43348,27 @@ var CalendarComponent = class CalendarComponent {
42256
43348
  isSignal: true,
42257
43349
  isRequired: false,
42258
43350
  transformFunction: null
43351
+ },
43352
+ dayDecorationFn: {
43353
+ classPropertyName: "dayDecorationFn",
43354
+ publicName: "dayDecorationFn",
43355
+ isSignal: true,
43356
+ isRequired: false,
43357
+ transformFunction: null
43358
+ },
43359
+ itemActionsFn: {
43360
+ classPropertyName: "itemActionsFn",
43361
+ publicName: "itemActionsFn",
43362
+ isSignal: true,
43363
+ isRequired: false,
43364
+ transformFunction: null
43365
+ },
43366
+ agendaGroupFn: {
43367
+ classPropertyName: "agendaGroupFn",
43368
+ publicName: "agendaGroupFn",
43369
+ isSignal: true,
43370
+ isRequired: false,
43371
+ transformFunction: null
42259
43372
  }
42260
43373
  },
42261
43374
  outputs: {
@@ -42266,7 +43379,8 @@ var CalendarComponent = class CalendarComponent {
42266
43379
  itemClick: "itemClick",
42267
43380
  dateClick: "dateClick",
42268
43381
  slotClick: "slotClick",
42269
- moreClick: "moreClick"
43382
+ moreClick: "moreClick",
43383
+ itemAction: "itemAction"
42270
43384
  },
42271
43385
  ngImport: i0,
42272
43386
  template: `
@@ -42374,6 +43488,90 @@ var CalendarComponent = class CalendarComponent {
42374
43488
  </div>
42375
43489
  </ng-template>
42376
43490
 
43491
+ <!-- Shared item markup: the same button is drawn on its own or, when itemActionsFn offers actions, inside a wrapper next to the action buttons
43492
+ (a button can never contain buttons). -->
43493
+ <ng-template #monthChip let-e>
43494
+ <button type="button" class="event-chip" [class.stacked]="chipLayout() === 'stacked'" [class.has-head]="!!(e.iconPath || chipTime(e))" [style.background]="e.bgColor" [style.borderLeftColor]="e.color"
43495
+ [attr.aria-label]="itemLabel(e)" [title]="e.tooltip || e.title"
43496
+ (click)="onItemClick(e, $event)">
43497
+ <span class="chip-head">
43498
+ @if (e.iconPath) {
43499
+ <svg class="chip-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path [attr.d]="e.iconPath"/></svg>
43500
+ }
43501
+ @if (chipTime(e); as t) { <span class="chip-time">{{ t }}</span> }
43502
+ </span>
43503
+ <span class="chip-title" [class.strike]="e.strike">{{ e.title }}</span>
43504
+ @if (e.tag) { <span class="chip-src">{{ e.tag }}</span> }
43505
+ </button>
43506
+ </ng-template>
43507
+ <ng-template #allDayChip let-e>
43508
+ <button type="button" class="allday-chip" [style.background]="e.bgColor" [style.borderLeftColor]="e.color"
43509
+ [attr.aria-label]="itemLabel(e)" [attr.title]="e.tooltip || null" (click)="onItemClick(e, $event)">{{ e.title }}</button>
43510
+ </ng-template>
43511
+ <ng-template #timedEvent let-t let-wrapped="wrapped">
43512
+ <button type="button" class="timed-event"
43513
+ [class.in-wrap]="wrapped" [style.top.px]="wrapped ? null : t.top" [style.height.px]="wrapped ? null : t.height"
43514
+ [style.left]="wrapped ? null : 'calc(' + (t.lane.lane / t.lane.lanes * 100) + '% + 2px)'"
43515
+ [style.width]="wrapped ? null : 'calc(' + (100 / t.lane.lanes) + '% - 4px)'"
43516
+ [style.background]="t.item.bgColor" [style.borderLeftColor]="t.item.color"
43517
+ [attr.aria-label]="itemLabel(t.item)" [attr.title]="t.item.tooltip || null" (click)="onItemClick(t.item, $event)">
43518
+ <span class="te-title" [class.strike]="t.item.strike">{{ t.item.title }}</span>
43519
+ @if (t.item.startTime) { <span class="te-time">{{ t.item.timeLabel || timeRange(t.item) }}</span> }
43520
+ </button>
43521
+ </ng-template>
43522
+ <ng-template #agendaRow let-e>
43523
+ <button type="button" class="agenda-event" [attr.aria-label]="e.ariaLabel || null" [attr.title]="e.tooltip || null" (click)="onItemClick(e, $event)">
43524
+ <span class="ae-color-bar" [style.background]="e.color"></span>
43525
+ <span class="ae-body">
43526
+ <span class="ae-top">
43527
+ <span class="ae-title" [class.strike]="e.strike">{{ e.title }}</span>
43528
+ @for (b of e.badges ?? []; track b.label) { <ui-badge [label]="b.label" [variant]="b.variant" /> }
43529
+ </span>
43530
+ @if (agendaMeta(e).length) {
43531
+ <span class="ae-meta">
43532
+ @for (m of agendaMeta(e); track $index) { <span class="ae-meta-item">{{ m }}</span> }
43533
+ </span>
43534
+ }
43535
+ </span>
43536
+ </button>
43537
+ </ng-template>
43538
+ <ng-template #agendaGroupTpl let-group>
43539
+ <div class="agenda-group">
43540
+ <div class="agenda-date-hdr">
43541
+ <span class="ag-dow">{{ agendaDow(group.dateStr) }}</span>
43542
+ <span class="ag-date">{{ agendaDate(group.dateStr) }}</span>
43543
+ @if (group.dateStr === todayStr()) { <ui-badge label="Today" variant="blue" /> }
43544
+ </div>
43545
+ <div class="agenda-events">
43546
+ @for (e of group.items; track e.id) {
43547
+ @if (actionsFor(e).length) {
43548
+ <div class="item-wrap agenda-wrap">
43549
+ <ng-container *ngTemplateOutlet="agendaRow; context: { $implicit: e }" />
43550
+ <ng-container *ngTemplateOutlet="itemActionsTpl; context: { $implicit: e }" />
43551
+ </div>
43552
+ } @else {
43553
+ <ng-container *ngTemplateOutlet="agendaRow; context: { $implicit: e }" />
43554
+ }
43555
+ }
43556
+ </div>
43557
+ </div>
43558
+
43559
+ </ng-template>
43560
+ <ng-template #itemActionsTpl let-e>
43561
+ <span class="item-actions" role="group" [attr.aria-label]="'Actions for ' + e.title" (keydown.escape)="onActionsEscape($event)">
43562
+ @for (a of actionsFor(e); track a.id) {
43563
+ <button type="button" class="item-action" [class.item-action--danger]="a.variant === 'danger'" [disabled]="!!a.disabled"
43564
+ [attr.aria-label]="a.label + ': ' + e.title" [attr.title]="a.label" (click)="onActionClick(e, a, $event)">
43565
+ @if (actionIconD(a); as d) {
43566
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="d" /></svg>
43567
+ } @else {
43568
+ <span class="item-action-text">{{ a.label }}</span>
43569
+ }
43570
+ </button>
43571
+ }
43572
+ </span>
43573
+ </ng-template>
43574
+
42377
43575
  <div class="cal-header">
42378
43576
  <div class="cal-toolbar">
42379
43577
  <div class="toolbar-left">
@@ -42457,6 +43655,8 @@ var CalendarComponent = class CalendarComponent {
42457
43655
  [class.clickable]="c.clickable"
42458
43656
  [attr.tabindex]="c.day.dateStr === focusDate() ? 0 : -1"
42459
43657
  [attr.aria-label]="c.label"
43658
+ [attr.data-level]="c.level"
43659
+ [uiTooltip]="c.tooltip"
42460
43660
  [attr.aria-current]="c.day.isToday ? 'date' : null"
42461
43661
  (click)="onDateClick(c.day.dateStr)"
42462
43662
  (focus)="focusedDate.set(c.day.dateStr)"
@@ -42464,18 +43664,14 @@ var CalendarComponent = class CalendarComponent {
42464
43664
  <span class="day-num" [class.today-badge]="c.day.isToday">{{ c.day.dayNum }}</span>
42465
43665
  <div class="cell-events">
42466
43666
  @for (e of c.visible; track e.id) {
42467
- <button type="button" class="event-chip" [class.stacked]="chipLayout() === 'stacked'" [class.has-head]="!!(e.iconPath || chipTime(e))" [style.background]="e.bgColor" [style.borderLeftColor]="e.color"
42468
- [attr.aria-label]="itemLabel(e)" [title]="e.tooltip || e.title"
42469
- (click)="onItemClick(e, $event)">
42470
- <span class="chip-head">
42471
- @if (e.iconPath) {
42472
- <svg class="chip-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path [attr.d]="e.iconPath"/></svg>
42473
- }
42474
- @if (chipTime(e); as t) { <span class="chip-time">{{ t }}</span> }
42475
- </span>
42476
- <span class="chip-title" [class.strike]="e.strike">{{ e.title }}</span>
42477
- @if (e.tag) { <span class="chip-src">{{ e.tag }}</span> }
42478
- </button>
43667
+ @if (actionsFor(e).length) {
43668
+ <div class="item-wrap">
43669
+ <ng-container *ngTemplateOutlet="monthChip; context: { $implicit: e }" />
43670
+ <ng-container *ngTemplateOutlet="itemActionsTpl; context: { $implicit: e }" />
43671
+ </div>
43672
+ } @else {
43673
+ <ng-container *ngTemplateOutlet="monthChip; context: { $implicit: e }" />
43674
+ }
42479
43675
  }
42480
43676
  @if (c.overflow > 0) {
42481
43677
  <button type="button" class="more-events" (click)="onMoreClick(c.day.dateStr, c.items, $event)">+{{ c.overflow }} more</button>
@@ -42493,9 +43689,10 @@ var CalendarComponent = class CalendarComponent {
42493
43689
  <div class="week-header">
42494
43690
  <div class="time-gutter"></div>
42495
43691
  @for (col of weekColumns(); track col.day.dateStr) {
42496
- <div class="week-day-hdr" [class.today]="col.day.isToday">
43692
+ <div class="week-day-hdr" [class.today]="col.day.isToday" [attr.data-level]="col.level" [uiTooltip]="col.tooltip">
42497
43693
  <span class="wdh-dow">{{ dowLabel(col.day.dateStr) }}</span>
42498
43694
  <span class="wdh-num" [class.today-badge]="col.day.isToday">{{ col.day.dayNum }}</span>
43695
+ @if (col.level) { <span class="deco-sr">{{ levelText(col.level) }}</span> }
42499
43696
  </div>
42500
43697
  }
42501
43698
  </div>
@@ -42507,8 +43704,14 @@ var CalendarComponent = class CalendarComponent {
42507
43704
  <button type="button" class="slot-add" [attr.aria-label]="'Add all-day event on ' + col.label" (click)="onAddAllDay(col.day.dateStr, $event)">+ Add</button>
42508
43705
  }
42509
43706
  @for (e of col.allDay; track e.id) {
42510
- <button type="button" class="allday-chip" [style.background]="e.bgColor" [style.borderLeftColor]="e.color"
42511
- [attr.aria-label]="itemLabel(e)" [attr.title]="e.tooltip || null" (click)="onItemClick(e, $event)">{{ e.title }}</button>
43707
+ @if (actionsFor(e).length) {
43708
+ <div class="item-wrap">
43709
+ <ng-container *ngTemplateOutlet="allDayChip; context: { $implicit: e }" />
43710
+ <ng-container *ngTemplateOutlet="itemActionsTpl; context: { $implicit: e }" />
43711
+ </div>
43712
+ } @else {
43713
+ <ng-container *ngTemplateOutlet="allDayChip; context: { $implicit: e }" />
43714
+ }
42512
43715
  }
42513
43716
  </div>
42514
43717
  }
@@ -42524,15 +43727,16 @@ var CalendarComponent = class CalendarComponent {
42524
43727
  }
42525
43728
  @for (h of hours(); track h) { <div class="time-slot"></div> }
42526
43729
  @for (t of col.timed; track t.item.id) {
42527
- <button type="button" class="timed-event"
42528
- [style.top.px]="t.top" [style.height.px]="t.height"
42529
- [style.left]="'calc(' + (t.lane.lane / t.lane.lanes * 100) + '% + 2px)'"
42530
- [style.width]="'calc(' + (100 / t.lane.lanes) + '% - 4px)'"
42531
- [style.background]="t.item.bgColor" [style.borderLeftColor]="t.item.color"
42532
- [attr.aria-label]="itemLabel(t.item)" [attr.title]="t.item.tooltip || null" (click)="onItemClick(t.item, $event)">
42533
- <span class="te-title" [class.strike]="t.item.strike">{{ t.item.title }}</span>
42534
- @if (t.item.startTime) { <span class="te-time">{{ t.item.timeLabel || timeRange(t.item) }}</span> }
42535
- </button>
43730
+ @if (actionsFor(t.item).length) {
43731
+ <div class="item-wrap timed-wrap" [style.top.px]="t.top" [style.height.px]="t.height"
43732
+ [style.left]="'calc(' + (t.lane.lane / t.lane.lanes * 100) + '% + 2px)'"
43733
+ [style.width]="'calc(' + (100 / t.lane.lanes) + '% - 4px)'">
43734
+ <ng-container *ngTemplateOutlet="timedEvent; context: { $implicit: t, wrapped: true }" />
43735
+ <ng-container *ngTemplateOutlet="itemActionsTpl; context: { $implicit: t.item }" />
43736
+ </div>
43737
+ } @else {
43738
+ <ng-container *ngTemplateOutlet="timedEvent; context: { $implicit: t, wrapped: false }" />
43739
+ }
42536
43740
  }
42537
43741
  </div>
42538
43742
  }
@@ -42544,32 +43748,25 @@ var CalendarComponent = class CalendarComponent {
42544
43748
  @if (agendaGroups().length === 0) {
42545
43749
  <ui-empty-state iconType="task" [message]="emptyMessage()" />
42546
43750
  }
42547
- @for (group of agendaGroups(); track group.dateStr) {
42548
- <div class="agenda-group">
42549
- <div class="agenda-date-hdr">
42550
- <span class="ag-dow">{{ agendaDow(group.dateStr) }}</span>
42551
- <span class="ag-date">{{ agendaDate(group.dateStr) }}</span>
42552
- @if (group.dateStr === todayStr()) { <ui-badge label="Today" variant="blue" /> }
42553
- </div>
42554
- <div class="agenda-events">
42555
- @for (e of group.items; track e.id) {
42556
- <button type="button" class="agenda-event" [attr.aria-label]="e.ariaLabel || null" [attr.title]="e.tooltip || null" (click)="onItemClick(e, $event)">
42557
- <span class="ae-color-bar" [style.background]="e.color"></span>
42558
- <span class="ae-body">
42559
- <span class="ae-top">
42560
- <span class="ae-title" [class.strike]="e.strike">{{ e.title }}</span>
42561
- @for (b of e.badges ?? []; track b.label) { <ui-badge [label]="b.label" [variant]="b.variant" /> }
42562
- </span>
42563
- @if (agendaMeta(e).length) {
42564
- <span class="ae-meta">
42565
- @for (m of agendaMeta(e); track $index) { <span class="ae-meta-item">{{ m }}</span> }
42566
- </span>
42567
- }
42568
- </span>
42569
- </button>
43751
+ @if (agendaSections(); as sections) {
43752
+ @for (s of sections; track $index) {
43753
+ @if (s.label) {
43754
+ <section class="agenda-section" [attr.aria-labelledby]="s.id">
43755
+ <h3 class="agenda-period-hdr" [id]="s.id">{{ s.label }}</h3>
43756
+ @for (group of s.groups; track group.dateStr) {
43757
+ <ng-container *ngTemplateOutlet="agendaGroupTpl; context: { $implicit: group }" />
43758
+ }
43759
+ </section>
43760
+ } @else {
43761
+ @for (group of s.groups; track group.dateStr) {
43762
+ <ng-container *ngTemplateOutlet="agendaGroupTpl; context: { $implicit: group }" />
42570
43763
  }
42571
- </div>
42572
- </div>
43764
+ }
43765
+ }
43766
+ } @else {
43767
+ @for (group of agendaGroups(); track group.dateStr) {
43768
+ <ng-container *ngTemplateOutlet="agendaGroupTpl; context: { $implicit: group }" />
43769
+ }
42573
43770
  }
42574
43771
  </div>
42575
43772
  }
@@ -42579,7 +43776,7 @@ var CalendarComponent = class CalendarComponent {
42579
43776
  <ui-toast />
42580
43777
  `,
42581
43778
  isInline: true,
42582
- styles: [":host{display:flex;flex-direction:column;gap:0;height:100%;min-height:0}.cal-header{background:#fff;border:1px solid #e2e8f0;border-radius:12px;box-shadow:0 1px 2px #0f172a0a;margin-bottom:12px;flex-shrink:0}.cal-toolbar{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;padding:18px 20px;gap:16px;flex-shrink:0}.toolbar-left{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;column-gap:20px;row-gap:8px;min-width:0}.toolbar-center{display:flex;align-items:center;justify-content:center;gap:10px}.toolbar-right{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}.toolbar-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:nowrap}.toolbar-actions:empty{display:none}ui-pill-tabs+.toolbar-actions:not(:empty){margin-left:4px;padding-left:12px;border-left:1px solid #e2e8f0;align-self:stretch}@media(max-width:480px){.toolbar-actions{flex-wrap:wrap}}.period-label{font-size:2.5rem;font-weight:800;line-height:1.15;letter-spacing:-.025em;color:#0f172a;margin:0;min-width:0}.pl-sub{font-weight:600;color:#64748b}.nav-controls{display:inline-flex;align-items:center}.nav-controls>*+*{margin-left:-1px}.btn-nav{width:38px;height:38px;border:1px solid #cbd5e1;background:#fff;font-family:inherit;position:relative;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#475569;transition:background .12s,color .12s}.nav-controls>.btn-nav:first-child{border-radius:10px 0 0 10px}.nav-controls>.btn-nav:last-child{border-radius:0 10px 10px 0}.btn-today{padding:0 18px;height:38px;border:1px solid #cbd5e1;background:#fff;border-radius:0;font-family:inherit;position:relative;font-size:.9375rem;font-weight:600;color:#1d4ed8;cursor:pointer;transition:background .12s}.btn-nav:hover,.btn-today:hover{background:#eff6ff;color:#1d4ed8;z-index:1}.btn-nav:active,.btn-today:active{background:#dbeafe}.btn-nav:focus-visible,.btn-today:focus-visible,.filter-chip:focus-visible,.event-chip:focus-visible,.more-events:focus-visible,.allday-chip:focus-visible,.timed-event:focus-visible,.agenda-event:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.month-cell:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.slot-add:focus,.slot-add:focus-visible{outline:2px solid #3b82f6;outline-offset:1px}.mobile-summary{display:none}.filter-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px 16px;padding:12px 20px 14px;border-top:1px solid #eef2f7;background:#f8fafc;border-radius:0 0 12px 12px;flex-shrink:0}.filter-main{display:flex;flex-direction:column;gap:8px;flex:1 1 auto;min-width:0}.filter-nav{margin-left:auto;flex-shrink:0}.filter-bar-label{font-size:.6875rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.06em}.filter-chips{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.filter-chip{display:inline-flex;align-items:center;gap:5px;padding:4px 12px;border-radius:20px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#475569;cursor:pointer;transition:all .12s;white-space:nowrap;font-family:inherit}.filter-chip:hover{border-color:#cbd5e1;background:#f8fafc}.filter-chip.active-all{background:#1e293b;color:#fff;border-color:#1e293b}.filter-chip.active-all .chip-count{background:#fff3;color:#fff}.filter-chip.active-type{background:var(--chip-bg, #f1f5f9);color:#1e293b;border-color:var(--chip-color);font-weight:600}.filter-chip.active-type .chip-count{background:var(--chip-color);color:#fff}.chip-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}.chip-count{font-size:.6875rem;font-weight:700;background:#f1f5f9;background:color-mix(in srgb,var(--chip-color, #64748b) 14%,#fff);color:#475569;padding:1px 8px;border-radius:999px;min-width:18px;text-align:center;box-sizing:border-box}.cal-skeleton,.outline-grid,.month-view,.week-view,.agenda-view{flex:1;display:flex;flex-direction:column;min-height:0;background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden}.skel-dow-row{display:grid;grid-template-columns:repeat(7,1fr);gap:8px;padding:12px 10px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.skel-dow{display:flex;justify-content:center}.skel-month-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:repeat(6,minmax(80px,1fr));flex:1}.skel-cell{border-right:1px solid #f1f5f9;border-bottom:1px solid #f1f5f9;padding:8px 6px;display:flex;flex-direction:column;gap:6px}.skel-cell:nth-child(7n){border-right:none}.cal-outline{flex:1;display:flex;flex-direction:column;min-height:0}.cal-outline ui-skeleton{display:block;flex-shrink:0}.skel-period-wrap{width:240px;max-width:100%;min-width:0}.skel-nav-controls{gap:2px}.skel-nav-controls>*+*{margin-left:0}.skel-tabs{display:flex;align-items:center;gap:6px}.skel-tabs+.toolbar-actions:not(:empty){margin-left:4px;padding-left:12px;border-left:1px solid #e2e8f0;align-self:stretch}.skel-week-grid{display:grid;grid-template-columns:repeat(7,1fr);flex:1;min-height:360px}.skel-agenda{display:flex;flex-direction:column;flex:1}.skel-row{display:flex;align-items:center;gap:16px;padding:16px 12px;border-bottom:1px solid #f1f5f9}.skel-row-body{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0}.month-grid{display:flex;flex-direction:column;flex:1;min-height:0;overflow:auto}.month-header{display:grid;grid-template-columns:repeat(7,1fr);border-bottom:1px solid #e2e8f0;background:#f8fafc;flex-shrink:0;position:sticky;top:0;z-index:1}.month-dow{padding:10px 8px;font-size:.75rem;font-weight:600;color:#6b7280;text-align:center;text-transform:uppercase;letter-spacing:.04em}.dow-short{display:none}.month-row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));flex:1 0 120px}.month-cell{border-right:1px solid #f1f5f9;border-bottom:1px solid #f1f5f9;padding:6px 6px 4px;min-height:120px;transition:background .1s;min-width:0;box-sizing:border-box}.month-cell.clickable{cursor:pointer}.month-cell.clickable:hover{background:#f8fafc}.month-cell:nth-child(7n){border-right:none}.month-cell.other-month{background:#fafafa}.month-cell.other-month .day-num{color:#64748b}.month-cell.today{background:#eff6ff}.day-num{display:inline-block;width:26px;height:26px;line-height:26px;text-align:center;border-radius:50%;font-size:.8125rem;font-weight:500;color:#374151;margin-bottom:4px}.today-badge{background:#2563eb;color:#fff;font-weight:700}.cell-events{display:flex;flex-direction:column;gap:2px}.event-chip{display:flex;align-items:center;gap:3px;padding:2px 6px;width:100%;box-sizing:border-box;text-align:left;font-family:inherit;border:0;border-radius:4px;border-left:3px solid transparent;font-size:.6875rem;font-weight:500;cursor:pointer;overflow:hidden}.event-chip:hover{opacity:.85}.event-chip.stacked{flex-direction:column;align-items:flex-start;gap:0}.chip-head{display:inline-flex;align-items:center;gap:3px;flex-shrink:0}.chip-icon{flex-shrink:0}.chip-time{font-weight:700;flex-shrink:0;color:#1e293b}.chip-title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#1e293b;min-width:0;max-width:100%}.strike{text-decoration:line-through}.chip-src{font-size:.625rem;font-weight:700;margin-left:auto;flex-shrink:0;color:#1e293b}.more-events{font-size:.6875rem;color:#2563eb;font-weight:600;cursor:pointer;padding:1px 6px;border:0;background:none;text-align:left;font-family:inherit}.more-events:hover{text-decoration:underline}.week-view{overflow:auto}.week-header{display:grid;grid-template-columns:60px repeat(7,1fr);border-bottom:1px solid #e2e8f0;background:#f8fafc;flex-shrink:0;overflow-y:scroll}.time-gutter{border-right:1px solid #e2e8f0}.week-day-hdr{display:flex;flex-direction:column;align-items:center;padding:10px 4px;border-right:1px solid #f1f5f9}.week-day-hdr.today{background:#eff6ff}.week-day-hdr:last-child{border-right:none}.wdh-dow{font-size:.6875rem;font-weight:600;color:#6b7280;text-transform:uppercase}.wdh-num{width:28px;height:28px;line-height:28px;text-align:center;border-radius:50%;font-size:.9375rem;font-weight:600;color:#374151;margin-top:2px}.wdh-num.today-badge{background:#2563eb;color:#fff;font-weight:700}.allday-row{display:grid;grid-template-columns:60px repeat(7,1fr);border-bottom:1px solid #e2e8f0;min-height:32px;flex-shrink:0;overflow-y:scroll}.allday-label{font-size:.625rem;color:#64748b;font-weight:600;text-transform:uppercase;display:flex;align-items:center;justify-content:center}.slot-add{position:absolute;top:2px;left:2px;z-index:3;width:1px;height:1px;padding:0;margin:0;overflow:hidden;clip-path:inset(50%);border:0;white-space:nowrap;font-family:inherit}.slot-add:focus,.slot-add:focus-visible{width:auto;height:auto;padding:2px 8px;clip-path:none;background:#1e293b;color:#fff;border-radius:4px;font-size:.75rem;font-weight:600}.allday-cell{position:relative;border-right:1px solid #f1f5f9;padding:3px 4px;display:flex;flex-direction:column;gap:2px;min-width:0}.allday-cell.clickable{cursor:pointer}.allday-cell:last-child{border-right:none}.allday-chip{padding:2px 6px;border:0;border-radius:4px;border-left:3px solid transparent;font-size:.6875rem;font-weight:500;color:#1e293b;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left;font-family:inherit}.allday-chip:hover{opacity:.85}.week-body{display:grid;grid-template-columns:60px repeat(7,1fr);flex:1;overflow-y:scroll}.time-col{display:flex;flex-direction:column;border-right:1px solid #e2e8f0;flex-shrink:0}.time-slot-label{height:60px;display:flex;align-items:flex-start;justify-content:flex-end;padding:4px 8px 0 0;font-size:.6875rem;color:#64748b;font-weight:500;border-bottom:1px solid #f1f5f9;flex-shrink:0;box-sizing:border-box}.day-col{position:relative;border-right:1px solid #f1f5f9;min-width:0}.day-col.clickable{cursor:pointer}.day-col:last-child{border-right:none}.day-col.today{background:#fafbff}.time-slot{height:60px;border-bottom:1px solid #f1f5f9;box-sizing:border-box}.day-col.clickable .time-slot:hover{background:#f8fafc}.timed-event{position:absolute;min-height:20px;border:0;border-radius:6px;padding:3px 6px;overflow:hidden;cursor:pointer;box-sizing:border-box;border-left:3px solid transparent;display:flex;flex-direction:column;align-items:flex-start;gap:1px;box-shadow:0 1px 3px #00000014;text-align:left;font-family:inherit}.timed-event:hover{opacity:.85}.te-title{font-size:.6875rem;font-weight:600;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.te-time{font-size:.625rem;color:#475569}.agenda-view{overflow-y:auto}.agenda-group{display:flex;border-bottom:1px solid #f1f5f9}.agenda-date-hdr{width:100px;flex-shrink:0;padding:16px 12px;display:flex;flex-direction:column;gap:4px;background:#f8fafc;border-right:2px solid #e2e8f0}.ag-dow{font-size:.6875rem;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.04em}.ag-date{font-size:.8125rem;font-weight:600;color:#374151}.agenda-events{flex:1;display:flex;flex-direction:column;min-width:0}.agenda-event{display:flex;align-items:stretch;padding:10px 12px;cursor:pointer;border:0;border-bottom:1px solid #f8fafc;background:none;text-align:left;width:100%;font-family:inherit;transition:background .1s}.agenda-event:hover{background:#f8fafc}.agenda-event:last-child{border-bottom:none}.ae-color-bar{display:block;width:4px;border-radius:4px;flex-shrink:0;margin-right:12px}.ae-body{flex:1;display:flex;flex-direction:column;gap:4px;min-width:0}.ae-top{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.ae-title{font-size:.9375rem;font-weight:600;color:#111827}.ae-meta{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.ae-meta-item{font-size:.8125rem;color:#6b7280}@media(prefers-reduced-motion:reduce){.btn-nav,.btn-today,.filter-chip,.month-cell,.agenda-event{transition:none}}@media(pointer:coarse),(max-width:480px){.btn-nav,.btn-today,.filter-chip,.event-chip,.more-events,.allday-chip,.agenda-event{min-height:44px}.btn-nav{min-width:44px}}@media(max-width:768px){.cal-toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 12px}.toolbar-left{flex:1 1 auto}.toolbar-right{flex:0 1 auto;min-width:0;max-width:100%}.toolbar-center{display:none}.period-label{font-size:1.875rem}.toolbar-right{justify-content:flex-end}.mobile-summary{display:block;margin-bottom:4px;flex-shrink:0}.mobile-summary:empty{display:none}.filter-chips{flex-wrap:nowrap;overflow-x:auto;padding-bottom:4px;-webkit-overflow-scrolling:touch}.filter-main{flex-basis:100%}.month-view,.cal-skeleton,.outline-grid,.week-view{overflow-x:auto;-webkit-overflow-scrolling:touch}.month-grid,.skel-month-grid,.skel-week-grid,.skel-dow-row{min-width:560px}.month-row{flex-basis:80px}.month-cell{min-height:80px}.week-header,.allday-row,.week-body{min-width:640px}.agenda-date-hdr{width:76px;padding:12px 8px}.ae-title{font-size:.875rem}}@media(max-width:640px){.event-chip.stacked.has-head .chip-title{display:none}}@media(max-width:640px){.cal-toolbar{padding:14px}.period-label{font-size:1.625rem}.filter-bar{padding:10px 14px 12px}.filter-nav{width:100%;margin-left:0}.filter-nav .nav-controls{display:flex;width:100%}.filter-nav .btn-today{flex:1 1 auto}.chip-head{flex-wrap:wrap;row-gap:0;flex-shrink:1;min-width:0}.chip-time{white-space:normal;line-height:1.1;flex-shrink:1;min-width:0;overflow-wrap:anywhere}.event-chip.stacked{flex-wrap:nowrap}.month-view,.cal-skeleton,.outline-grid{overflow-x:hidden}.month-grid,.skel-month-grid,.skel-week-grid,.skel-dow-row{min-width:0}.month-dow{padding:8px 2px;font-size:.6875rem;letter-spacing:0}.month-row{flex-basis:72px}.month-cell{min-height:72px;padding:3px 2px 2px}.day-num{width:22px;height:22px;line-height:22px;font-size:.75rem;margin-bottom:2px}.cell-events{gap:1px}.event-chip{min-height:24px;padding:1px 3px;font-size:.625rem;gap:2px}.more-events{min-height:24px;padding:1px 3px;font-size:.625rem;white-space:normal;line-height:1.1;max-width:100%}}@media(max-width:480px){.dow-full{display:none}.dow-short{display:inline}}\n"],
43779
+ styles: [":host{display:flex;flex-direction:column;gap:0;height:100%;min-height:0}.cal-header{background:#fff;border:1px solid #e2e8f0;border-radius:12px;box-shadow:0 1px 2px #0f172a0a;margin-bottom:12px;flex-shrink:0}.cal-toolbar{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;padding:18px 20px;gap:16px;flex-shrink:0}.toolbar-left{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;column-gap:20px;row-gap:8px;min-width:0}.toolbar-center{display:flex;align-items:center;justify-content:center;gap:10px}.toolbar-right{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}.toolbar-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:nowrap}.toolbar-actions:empty{display:none}ui-pill-tabs+.toolbar-actions:not(:empty){margin-left:4px;padding-left:12px;border-left:1px solid #e2e8f0;align-self:stretch}@media(max-width:480px){.toolbar-actions{flex-wrap:wrap}}.period-label{font-size:2.5rem;font-weight:800;line-height:1.15;letter-spacing:-.025em;color:#0f172a;margin:0;min-width:0}.pl-sub{font-weight:600;color:#64748b}.nav-controls{display:inline-flex;align-items:center}.nav-controls>*+*{margin-left:-1px}.btn-nav{width:38px;height:38px;border:1px solid #cbd5e1;background:#fff;font-family:inherit;position:relative;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#475569;transition:background .12s,color .12s}.nav-controls>.btn-nav:first-child{border-radius:10px 0 0 10px}.nav-controls>.btn-nav:last-child{border-radius:0 10px 10px 0}.btn-today{padding:0 18px;height:38px;border:1px solid #cbd5e1;background:#fff;border-radius:0;font-family:inherit;position:relative;font-size:.9375rem;font-weight:600;color:#1d4ed8;cursor:pointer;transition:background .12s}.btn-nav:hover,.btn-today:hover{background:#eff6ff;color:#1d4ed8;z-index:1}.btn-nav:active,.btn-today:active{background:#dbeafe}.btn-nav:focus-visible,.btn-today:focus-visible,.filter-chip:focus-visible,.event-chip:focus-visible,.more-events:focus-visible,.allday-chip:focus-visible,.timed-event:focus-visible,.agenda-event:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.month-cell:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.slot-add:focus,.slot-add:focus-visible{outline:2px solid #3b82f6;outline-offset:1px}.mobile-summary{display:none}.filter-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px 16px;padding:12px 20px 14px;border-top:1px solid #eef2f7;background:#f8fafc;border-radius:0 0 12px 12px;flex-shrink:0}.filter-main{display:flex;flex-direction:column;gap:8px;flex:1 1 auto;min-width:0}.filter-nav{margin-left:auto;flex-shrink:0}.filter-bar-label{font-size:.6875rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.06em}.filter-chips{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.filter-chip{display:inline-flex;align-items:center;gap:5px;padding:4px 12px;border-radius:20px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#475569;cursor:pointer;transition:all .12s;white-space:nowrap;font-family:inherit}.filter-chip:hover{border-color:#cbd5e1;background:#f8fafc}.filter-chip.active-all{background:#1e293b;color:#fff;border-color:#1e293b}.filter-chip.active-all .chip-count{background:#fff3;color:#fff}.filter-chip.active-type{background:var(--chip-bg, #f1f5f9);color:#1e293b;border-color:var(--chip-color);font-weight:600}.filter-chip.active-type .chip-count{background:var(--chip-color);color:#fff}.chip-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}.chip-count{font-size:.6875rem;font-weight:700;background:#f1f5f9;background:color-mix(in srgb,var(--chip-color, #64748b) 14%,#fff);color:#475569;padding:1px 8px;border-radius:999px;min-width:18px;text-align:center;box-sizing:border-box}.cal-skeleton,.outline-grid,.month-view,.week-view,.agenda-view{flex:1;display:flex;flex-direction:column;min-height:0;background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden}.skel-dow-row{display:grid;grid-template-columns:repeat(7,1fr);gap:8px;padding:12px 10px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.skel-dow{display:flex;justify-content:center}.skel-month-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:repeat(6,minmax(80px,1fr));flex:1}.skel-cell{border-right:1px solid #f1f5f9;border-bottom:1px solid #f1f5f9;padding:8px 6px;display:flex;flex-direction:column;gap:6px}.skel-cell:nth-child(7n){border-right:none}.cal-outline{flex:1;display:flex;flex-direction:column;min-height:0}.cal-outline ui-skeleton{display:block;flex-shrink:0}.skel-period-wrap{width:240px;max-width:100%;min-width:0}.skel-nav-controls{gap:2px}.skel-nav-controls>*+*{margin-left:0}.skel-tabs{display:flex;align-items:center;gap:6px}.skel-tabs+.toolbar-actions:not(:empty){margin-left:4px;padding-left:12px;border-left:1px solid #e2e8f0;align-self:stretch}.skel-week-grid{display:grid;grid-template-columns:repeat(7,1fr);flex:1;min-height:360px}.skel-agenda{display:flex;flex-direction:column;flex:1}.skel-row{display:flex;align-items:center;gap:16px;padding:16px 12px;border-bottom:1px solid #f1f5f9}.skel-row-body{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0}.month-grid{display:flex;flex-direction:column;flex:1;min-height:0;overflow:auto}.month-header{display:grid;grid-template-columns:repeat(7,1fr);border-bottom:1px solid #e2e8f0;background:#f8fafc;flex-shrink:0;position:sticky;top:0;z-index:1}.month-dow{padding:10px 8px;font-size:.75rem;font-weight:600;color:#6b7280;text-align:center;text-transform:uppercase;letter-spacing:.04em}.dow-short{display:none}.month-row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));flex:1 0 120px}.month-cell{border-right:1px solid #f1f5f9;border-bottom:1px solid #f1f5f9;padding:6px 6px 4px;min-height:120px;transition:background .1s;min-width:0;box-sizing:border-box}.month-cell.clickable{cursor:pointer}.month-cell.clickable:hover{background:#f8fafc}.month-cell:nth-child(7n){border-right:none}.month-cell.other-month{background:#fafafa}.month-cell.other-month .day-num{color:#64748b}.month-cell[data-level=low]{background:#f0fdf4;box-shadow:inset 0 3px #86efac}.month-cell[data-level=medium]{background:#fefce8;box-shadow:inset 0 3px #facc15}.month-cell[data-level=high]{background:#fff7ed;box-shadow:inset 0 3px #fb923c}.month-cell[data-level=critical]{background:#fef2f2;box-shadow:inset 0 3px #ef4444}.month-cell.other-month[data-level] .day-num{color:#475569}.month-cell.today{background:#eff6ff}.day-num{display:inline-block;width:26px;height:26px;line-height:26px;text-align:center;border-radius:50%;font-size:.8125rem;font-weight:500;color:#374151;margin-bottom:4px}.today-badge{background:#2563eb;color:#fff;font-weight:700}.cell-events{display:flex;flex-direction:column;gap:2px}.event-chip{display:flex;align-items:center;gap:3px;padding:2px 6px;width:100%;box-sizing:border-box;text-align:left;font-family:inherit;border:0;border-radius:4px;border-left:3px solid transparent;font-size:.6875rem;font-weight:500;cursor:pointer;overflow:hidden}.event-chip:hover{opacity:.85}.event-chip.stacked{flex-direction:column;align-items:flex-start;gap:0}.chip-head{display:inline-flex;align-items:center;gap:3px;flex-shrink:0}.chip-icon{flex-shrink:0}.chip-time{font-weight:700;flex-shrink:0;color:#1e293b}.chip-title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#1e293b;min-width:0;max-width:100%}.strike{text-decoration:line-through}.chip-src{font-size:.625rem;font-weight:700;margin-left:auto;flex-shrink:0;color:#1e293b}.more-events{font-size:.6875rem;color:#2563eb;font-weight:600;cursor:pointer;padding:1px 6px;border:0;background:none;text-align:left;font-family:inherit}.more-events:hover{text-decoration:underline}.week-view{overflow:auto}.week-header{display:grid;grid-template-columns:60px repeat(7,1fr);border-bottom:1px solid #e2e8f0;background:#f8fafc;flex-shrink:0;overflow-y:scroll}.time-gutter{border-right:1px solid #e2e8f0}.week-day-hdr{display:flex;flex-direction:column;align-items:center;padding:10px 4px;border-right:1px solid #f1f5f9}.week-day-hdr[data-level=low]{background:#f0fdf4;box-shadow:inset 0 3px #86efac}.week-day-hdr[data-level=medium]{background:#fefce8;box-shadow:inset 0 3px #facc15}.week-day-hdr[data-level=high]{background:#fff7ed;box-shadow:inset 0 3px #fb923c}.week-day-hdr[data-level=critical]{background:#fef2f2;box-shadow:inset 0 3px #ef4444}.week-day-hdr.today{background:#eff6ff}.deco-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);border:0;white-space:nowrap}.week-day-hdr:last-child{border-right:none}.wdh-dow{font-size:.6875rem;font-weight:600;color:#6b7280;text-transform:uppercase}.wdh-num{width:28px;height:28px;line-height:28px;text-align:center;border-radius:50%;font-size:.9375rem;font-weight:600;color:#374151;margin-top:2px}.wdh-num.today-badge{background:#2563eb;color:#fff;font-weight:700}.allday-row{display:grid;grid-template-columns:60px repeat(7,1fr);border-bottom:1px solid #e2e8f0;min-height:32px;flex-shrink:0;overflow-y:scroll}.allday-label{font-size:.625rem;color:#64748b;font-weight:600;text-transform:uppercase;display:flex;align-items:center;justify-content:center}.slot-add{position:absolute;top:2px;left:2px;z-index:3;width:1px;height:1px;padding:0;margin:0;overflow:hidden;clip-path:inset(50%);border:0;white-space:nowrap;font-family:inherit}.slot-add:focus,.slot-add:focus-visible{width:auto;height:auto;padding:2px 8px;clip-path:none;background:#1e293b;color:#fff;border-radius:4px;font-size:.75rem;font-weight:600}.allday-cell{position:relative;border-right:1px solid #f1f5f9;padding:3px 4px;display:flex;flex-direction:column;gap:2px;min-width:0}.allday-cell.clickable{cursor:pointer}.allday-cell:last-child{border-right:none}.allday-chip{padding:2px 6px;border:0;border-radius:4px;border-left:3px solid transparent;font-size:.6875rem;font-weight:500;color:#1e293b;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left;font-family:inherit}.allday-chip:hover{opacity:.85}.week-body{display:grid;grid-template-columns:60px repeat(7,1fr);flex:1;overflow-y:scroll}.time-col{display:flex;flex-direction:column;border-right:1px solid #e2e8f0;flex-shrink:0}.time-slot-label{height:60px;display:flex;align-items:flex-start;justify-content:flex-end;padding:4px 8px 0 0;font-size:.6875rem;color:#64748b;font-weight:500;border-bottom:1px solid #f1f5f9;flex-shrink:0;box-sizing:border-box}.day-col{position:relative;border-right:1px solid #f1f5f9;min-width:0}.day-col.clickable{cursor:pointer}.day-col:last-child{border-right:none}.day-col.today{background:#fafbff}.time-slot{height:60px;border-bottom:1px solid #f1f5f9;box-sizing:border-box}.day-col.clickable .time-slot:hover{background:#f8fafc}.timed-event{position:absolute;min-height:20px;border:0;border-radius:6px;padding:3px 6px;overflow:hidden;cursor:pointer;box-sizing:border-box;border-left:3px solid transparent;display:flex;flex-direction:column;align-items:flex-start;gap:1px;box-shadow:0 1px 3px #00000014;text-align:left;font-family:inherit}.timed-event:hover{opacity:.85}.te-title{font-size:.6875rem;font-weight:600;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.te-time{font-size:.625rem;color:#475569}.agenda-view{overflow-y:auto}.agenda-section{display:block}.agenda-period-hdr{margin:0;padding:8px 12px;font-size:.75rem;font-weight:700;color:#475569;text-transform:uppercase;letter-spacing:.06em;background:#f1f5f9;border-bottom:1px solid #e2e8f0;position:-webkit-sticky;position:sticky;top:0;z-index:1}.agenda-group{display:flex;border-bottom:1px solid #f1f5f9}.agenda-date-hdr{width:100px;flex-shrink:0;padding:16px 12px;display:flex;flex-direction:column;gap:4px;background:#f8fafc;border-right:2px solid #e2e8f0}.ag-dow{font-size:.6875rem;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.04em}.ag-date{font-size:.8125rem;font-weight:600;color:#374151}.agenda-events{flex:1;display:flex;flex-direction:column;min-width:0}.agenda-event{display:flex;align-items:stretch;padding:10px 12px;cursor:pointer;border:0;border-bottom:1px solid #f8fafc;background:none;text-align:left;width:100%;font-family:inherit;transition:background .1s}.agenda-event:hover{background:#f8fafc}.agenda-event:last-child{border-bottom:none}.ae-color-bar{display:block;width:4px;border-radius:4px;flex-shrink:0;margin-right:12px}.ae-body{flex:1;display:flex;flex-direction:column;gap:4px;min-width:0}.ae-top{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.ae-title{font-size:.9375rem;font-weight:600;color:#111827}.ae-meta{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.ae-meta-item{font-size:.8125rem;color:#6b7280}.item-wrap{position:relative;display:block;min-width:0}.item-wrap.timed-wrap{position:absolute;min-height:20px}.timed-event.in-wrap{position:static;width:100%;height:100%}.item-actions{position:absolute;top:50%;right:2px;transform:translateY(-50%);display:inline-flex;gap:2px;opacity:0;pointer-events:none;z-index:2}.item-wrap:hover>.item-actions,.item-wrap:focus-within>.item-actions{opacity:1;pointer-events:auto}.agenda-wrap>.item-actions{right:12px}.item-action{min-width:20px;height:20px;padding:0 4px;display:inline-flex;align-items:center;justify-content:center;font-family:inherit;font-size:.6875rem;font-weight:600;color:#334155;background:#fff;border:1px solid #cbd5e1;border-radius:4px;cursor:pointer}.item-action:hover:not(:disabled){background:#eff6ff;color:#1d4ed8}.item-action--danger{color:#b91c1c;border-color:#fecaca}.item-action--danger:hover:not(:disabled){background:#fef2f2;color:#991b1b}.item-action:disabled{opacity:.5;cursor:not-allowed}.item-action:focus-visible{outline:2px solid #3b82f6;outline-offset:1px}@media(hover:none){.agenda-wrap>.item-actions{opacity:1;pointer-events:auto}}@media(pointer:coarse),(max-width:480px){.item-action{min-width:44px;min-height:44px}}@media(prefers-reduced-motion:reduce){.btn-nav,.btn-today,.filter-chip,.month-cell,.agenda-event{transition:none}}@media(pointer:coarse),(max-width:480px){.btn-nav,.btn-today,.filter-chip,.event-chip,.more-events,.allday-chip,.agenda-event{min-height:44px}.btn-nav{min-width:44px}}@media(max-width:768px){.cal-toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 12px}.toolbar-left{flex:1 1 auto}.toolbar-right{flex:0 1 auto;min-width:0;max-width:100%}.toolbar-center{display:none}.period-label{font-size:1.875rem}.toolbar-right{justify-content:flex-end}.mobile-summary{display:block;margin-bottom:4px;flex-shrink:0}.mobile-summary:empty{display:none}.filter-chips{flex-wrap:nowrap;overflow-x:auto;padding-bottom:4px;-webkit-overflow-scrolling:touch}.filter-main{flex-basis:100%}.month-view,.cal-skeleton,.outline-grid,.week-view{overflow-x:auto;-webkit-overflow-scrolling:touch}.month-grid,.skel-month-grid,.skel-week-grid,.skel-dow-row{min-width:560px}.month-row{flex-basis:80px}.month-cell{min-height:80px}.week-header,.allday-row,.week-body{min-width:640px}.agenda-date-hdr{width:76px;padding:12px 8px}.ae-title{font-size:.875rem}}@media(max-width:640px){.event-chip.stacked.has-head .chip-title{display:none}}@media(max-width:640px){.cal-toolbar{padding:14px}.period-label{font-size:1.625rem}.filter-bar{padding:10px 14px 12px}.filter-nav{width:100%;margin-left:0}.filter-nav .nav-controls{display:flex;width:100%}.filter-nav .btn-today{flex:1 1 auto}.chip-head{flex-wrap:wrap;row-gap:0;flex-shrink:1;min-width:0}.chip-time{white-space:normal;line-height:1.1;flex-shrink:1;min-width:0;overflow-wrap:anywhere}.event-chip.stacked{flex-wrap:nowrap}.month-view,.cal-skeleton,.outline-grid{overflow-x:hidden}.month-grid,.skel-month-grid,.skel-week-grid,.skel-dow-row{min-width:0}.month-dow{padding:8px 2px;font-size:.6875rem;letter-spacing:0}.month-row{flex-basis:72px}.month-cell{min-height:72px;padding:3px 2px 2px}.day-num{width:22px;height:22px;line-height:22px;font-size:.75rem;margin-bottom:2px}.cell-events{gap:1px}.event-chip{min-height:24px;padding:1px 3px;font-size:.625rem;gap:2px}.more-events{min-height:24px;padding:1px 3px;font-size:.625rem;white-space:normal;line-height:1.1;max-width:100%}}@media(max-width:480px){.dow-full{display:none}.dow-short{display:inline}}\n"],
42583
43780
  dependencies: [
42584
43781
  {
42585
43782
  kind: "component",
@@ -42637,7 +43834,22 @@ var CalendarComponent = class CalendarComponent {
42637
43834
  kind: "component",
42638
43835
  type: ToastComponent,
42639
43836
  selector: "ui-toast",
42640
- inputs: ["dismissible", "persistentLiveRegion"]
43837
+ inputs: [
43838
+ "dismissible",
43839
+ "persistentLiveRegion",
43840
+ "queue",
43841
+ "countdown",
43842
+ "maxQueued",
43843
+ "countdownLabel",
43844
+ "queueLabel"
43845
+ ],
43846
+ outputs: ["action"]
43847
+ },
43848
+ {
43849
+ kind: "directive",
43850
+ type: TooltipDirective,
43851
+ selector: "[uiTooltip]",
43852
+ inputs: ["uiTooltip", "tooltipPosition"]
42641
43853
  }
42642
43854
  ],
42643
43855
  changeDetection: i0.ChangeDetectionStrategy.OnPush
@@ -42661,7 +43873,8 @@ i0.ɵɵngDeclareClassMetadata({
42661
43873
  NgTemplateOutlet,
42662
43874
  PillTabsComponent,
42663
43875
  SkeletonComponent,
42664
- ToastComponent
43876
+ ToastComponent,
43877
+ TooltipDirective
42665
43878
  ],
42666
43879
  template: `
42667
43880
  @if (showOutline()) {
@@ -42768,6 +43981,90 @@ i0.ɵɵngDeclareClassMetadata({
42768
43981
  </div>
42769
43982
  </ng-template>
42770
43983
 
43984
+ <!-- Shared item markup: the same button is drawn on its own or, when itemActionsFn offers actions, inside a wrapper next to the action buttons
43985
+ (a button can never contain buttons). -->
43986
+ <ng-template #monthChip let-e>
43987
+ <button type="button" class="event-chip" [class.stacked]="chipLayout() === 'stacked'" [class.has-head]="!!(e.iconPath || chipTime(e))" [style.background]="e.bgColor" [style.borderLeftColor]="e.color"
43988
+ [attr.aria-label]="itemLabel(e)" [title]="e.tooltip || e.title"
43989
+ (click)="onItemClick(e, $event)">
43990
+ <span class="chip-head">
43991
+ @if (e.iconPath) {
43992
+ <svg class="chip-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path [attr.d]="e.iconPath"/></svg>
43993
+ }
43994
+ @if (chipTime(e); as t) { <span class="chip-time">{{ t }}</span> }
43995
+ </span>
43996
+ <span class="chip-title" [class.strike]="e.strike">{{ e.title }}</span>
43997
+ @if (e.tag) { <span class="chip-src">{{ e.tag }}</span> }
43998
+ </button>
43999
+ </ng-template>
44000
+ <ng-template #allDayChip let-e>
44001
+ <button type="button" class="allday-chip" [style.background]="e.bgColor" [style.borderLeftColor]="e.color"
44002
+ [attr.aria-label]="itemLabel(e)" [attr.title]="e.tooltip || null" (click)="onItemClick(e, $event)">{{ e.title }}</button>
44003
+ </ng-template>
44004
+ <ng-template #timedEvent let-t let-wrapped="wrapped">
44005
+ <button type="button" class="timed-event"
44006
+ [class.in-wrap]="wrapped" [style.top.px]="wrapped ? null : t.top" [style.height.px]="wrapped ? null : t.height"
44007
+ [style.left]="wrapped ? null : 'calc(' + (t.lane.lane / t.lane.lanes * 100) + '% + 2px)'"
44008
+ [style.width]="wrapped ? null : 'calc(' + (100 / t.lane.lanes) + '% - 4px)'"
44009
+ [style.background]="t.item.bgColor" [style.borderLeftColor]="t.item.color"
44010
+ [attr.aria-label]="itemLabel(t.item)" [attr.title]="t.item.tooltip || null" (click)="onItemClick(t.item, $event)">
44011
+ <span class="te-title" [class.strike]="t.item.strike">{{ t.item.title }}</span>
44012
+ @if (t.item.startTime) { <span class="te-time">{{ t.item.timeLabel || timeRange(t.item) }}</span> }
44013
+ </button>
44014
+ </ng-template>
44015
+ <ng-template #agendaRow let-e>
44016
+ <button type="button" class="agenda-event" [attr.aria-label]="e.ariaLabel || null" [attr.title]="e.tooltip || null" (click)="onItemClick(e, $event)">
44017
+ <span class="ae-color-bar" [style.background]="e.color"></span>
44018
+ <span class="ae-body">
44019
+ <span class="ae-top">
44020
+ <span class="ae-title" [class.strike]="e.strike">{{ e.title }}</span>
44021
+ @for (b of e.badges ?? []; track b.label) { <ui-badge [label]="b.label" [variant]="b.variant" /> }
44022
+ </span>
44023
+ @if (agendaMeta(e).length) {
44024
+ <span class="ae-meta">
44025
+ @for (m of agendaMeta(e); track $index) { <span class="ae-meta-item">{{ m }}</span> }
44026
+ </span>
44027
+ }
44028
+ </span>
44029
+ </button>
44030
+ </ng-template>
44031
+ <ng-template #agendaGroupTpl let-group>
44032
+ <div class="agenda-group">
44033
+ <div class="agenda-date-hdr">
44034
+ <span class="ag-dow">{{ agendaDow(group.dateStr) }}</span>
44035
+ <span class="ag-date">{{ agendaDate(group.dateStr) }}</span>
44036
+ @if (group.dateStr === todayStr()) { <ui-badge label="Today" variant="blue" /> }
44037
+ </div>
44038
+ <div class="agenda-events">
44039
+ @for (e of group.items; track e.id) {
44040
+ @if (actionsFor(e).length) {
44041
+ <div class="item-wrap agenda-wrap">
44042
+ <ng-container *ngTemplateOutlet="agendaRow; context: { $implicit: e }" />
44043
+ <ng-container *ngTemplateOutlet="itemActionsTpl; context: { $implicit: e }" />
44044
+ </div>
44045
+ } @else {
44046
+ <ng-container *ngTemplateOutlet="agendaRow; context: { $implicit: e }" />
44047
+ }
44048
+ }
44049
+ </div>
44050
+ </div>
44051
+
44052
+ </ng-template>
44053
+ <ng-template #itemActionsTpl let-e>
44054
+ <span class="item-actions" role="group" [attr.aria-label]="'Actions for ' + e.title" (keydown.escape)="onActionsEscape($event)">
44055
+ @for (a of actionsFor(e); track a.id) {
44056
+ <button type="button" class="item-action" [class.item-action--danger]="a.variant === 'danger'" [disabled]="!!a.disabled"
44057
+ [attr.aria-label]="a.label + ': ' + e.title" [attr.title]="a.label" (click)="onActionClick(e, a, $event)">
44058
+ @if (actionIconD(a); as d) {
44059
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="d" /></svg>
44060
+ } @else {
44061
+ <span class="item-action-text">{{ a.label }}</span>
44062
+ }
44063
+ </button>
44064
+ }
44065
+ </span>
44066
+ </ng-template>
44067
+
42771
44068
  <div class="cal-header">
42772
44069
  <div class="cal-toolbar">
42773
44070
  <div class="toolbar-left">
@@ -42851,6 +44148,8 @@ i0.ɵɵngDeclareClassMetadata({
42851
44148
  [class.clickable]="c.clickable"
42852
44149
  [attr.tabindex]="c.day.dateStr === focusDate() ? 0 : -1"
42853
44150
  [attr.aria-label]="c.label"
44151
+ [attr.data-level]="c.level"
44152
+ [uiTooltip]="c.tooltip"
42854
44153
  [attr.aria-current]="c.day.isToday ? 'date' : null"
42855
44154
  (click)="onDateClick(c.day.dateStr)"
42856
44155
  (focus)="focusedDate.set(c.day.dateStr)"
@@ -42858,18 +44157,14 @@ i0.ɵɵngDeclareClassMetadata({
42858
44157
  <span class="day-num" [class.today-badge]="c.day.isToday">{{ c.day.dayNum }}</span>
42859
44158
  <div class="cell-events">
42860
44159
  @for (e of c.visible; track e.id) {
42861
- <button type="button" class="event-chip" [class.stacked]="chipLayout() === 'stacked'" [class.has-head]="!!(e.iconPath || chipTime(e))" [style.background]="e.bgColor" [style.borderLeftColor]="e.color"
42862
- [attr.aria-label]="itemLabel(e)" [title]="e.tooltip || e.title"
42863
- (click)="onItemClick(e, $event)">
42864
- <span class="chip-head">
42865
- @if (e.iconPath) {
42866
- <svg class="chip-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path [attr.d]="e.iconPath"/></svg>
42867
- }
42868
- @if (chipTime(e); as t) { <span class="chip-time">{{ t }}</span> }
42869
- </span>
42870
- <span class="chip-title" [class.strike]="e.strike">{{ e.title }}</span>
42871
- @if (e.tag) { <span class="chip-src">{{ e.tag }}</span> }
42872
- </button>
44160
+ @if (actionsFor(e).length) {
44161
+ <div class="item-wrap">
44162
+ <ng-container *ngTemplateOutlet="monthChip; context: { $implicit: e }" />
44163
+ <ng-container *ngTemplateOutlet="itemActionsTpl; context: { $implicit: e }" />
44164
+ </div>
44165
+ } @else {
44166
+ <ng-container *ngTemplateOutlet="monthChip; context: { $implicit: e }" />
44167
+ }
42873
44168
  }
42874
44169
  @if (c.overflow > 0) {
42875
44170
  <button type="button" class="more-events" (click)="onMoreClick(c.day.dateStr, c.items, $event)">+{{ c.overflow }} more</button>
@@ -42887,9 +44182,10 @@ i0.ɵɵngDeclareClassMetadata({
42887
44182
  <div class="week-header">
42888
44183
  <div class="time-gutter"></div>
42889
44184
  @for (col of weekColumns(); track col.day.dateStr) {
42890
- <div class="week-day-hdr" [class.today]="col.day.isToday">
44185
+ <div class="week-day-hdr" [class.today]="col.day.isToday" [attr.data-level]="col.level" [uiTooltip]="col.tooltip">
42891
44186
  <span class="wdh-dow">{{ dowLabel(col.day.dateStr) }}</span>
42892
44187
  <span class="wdh-num" [class.today-badge]="col.day.isToday">{{ col.day.dayNum }}</span>
44188
+ @if (col.level) { <span class="deco-sr">{{ levelText(col.level) }}</span> }
42893
44189
  </div>
42894
44190
  }
42895
44191
  </div>
@@ -42901,8 +44197,14 @@ i0.ɵɵngDeclareClassMetadata({
42901
44197
  <button type="button" class="slot-add" [attr.aria-label]="'Add all-day event on ' + col.label" (click)="onAddAllDay(col.day.dateStr, $event)">+ Add</button>
42902
44198
  }
42903
44199
  @for (e of col.allDay; track e.id) {
42904
- <button type="button" class="allday-chip" [style.background]="e.bgColor" [style.borderLeftColor]="e.color"
42905
- [attr.aria-label]="itemLabel(e)" [attr.title]="e.tooltip || null" (click)="onItemClick(e, $event)">{{ e.title }}</button>
44200
+ @if (actionsFor(e).length) {
44201
+ <div class="item-wrap">
44202
+ <ng-container *ngTemplateOutlet="allDayChip; context: { $implicit: e }" />
44203
+ <ng-container *ngTemplateOutlet="itemActionsTpl; context: { $implicit: e }" />
44204
+ </div>
44205
+ } @else {
44206
+ <ng-container *ngTemplateOutlet="allDayChip; context: { $implicit: e }" />
44207
+ }
42906
44208
  }
42907
44209
  </div>
42908
44210
  }
@@ -42918,15 +44220,16 @@ i0.ɵɵngDeclareClassMetadata({
42918
44220
  }
42919
44221
  @for (h of hours(); track h) { <div class="time-slot"></div> }
42920
44222
  @for (t of col.timed; track t.item.id) {
42921
- <button type="button" class="timed-event"
42922
- [style.top.px]="t.top" [style.height.px]="t.height"
42923
- [style.left]="'calc(' + (t.lane.lane / t.lane.lanes * 100) + '% + 2px)'"
42924
- [style.width]="'calc(' + (100 / t.lane.lanes) + '% - 4px)'"
42925
- [style.background]="t.item.bgColor" [style.borderLeftColor]="t.item.color"
42926
- [attr.aria-label]="itemLabel(t.item)" [attr.title]="t.item.tooltip || null" (click)="onItemClick(t.item, $event)">
42927
- <span class="te-title" [class.strike]="t.item.strike">{{ t.item.title }}</span>
42928
- @if (t.item.startTime) { <span class="te-time">{{ t.item.timeLabel || timeRange(t.item) }}</span> }
42929
- </button>
44223
+ @if (actionsFor(t.item).length) {
44224
+ <div class="item-wrap timed-wrap" [style.top.px]="t.top" [style.height.px]="t.height"
44225
+ [style.left]="'calc(' + (t.lane.lane / t.lane.lanes * 100) + '% + 2px)'"
44226
+ [style.width]="'calc(' + (100 / t.lane.lanes) + '% - 4px)'">
44227
+ <ng-container *ngTemplateOutlet="timedEvent; context: { $implicit: t, wrapped: true }" />
44228
+ <ng-container *ngTemplateOutlet="itemActionsTpl; context: { $implicit: t.item }" />
44229
+ </div>
44230
+ } @else {
44231
+ <ng-container *ngTemplateOutlet="timedEvent; context: { $implicit: t, wrapped: false }" />
44232
+ }
42930
44233
  }
42931
44234
  </div>
42932
44235
  }
@@ -42938,32 +44241,25 @@ i0.ɵɵngDeclareClassMetadata({
42938
44241
  @if (agendaGroups().length === 0) {
42939
44242
  <ui-empty-state iconType="task" [message]="emptyMessage()" />
42940
44243
  }
42941
- @for (group of agendaGroups(); track group.dateStr) {
42942
- <div class="agenda-group">
42943
- <div class="agenda-date-hdr">
42944
- <span class="ag-dow">{{ agendaDow(group.dateStr) }}</span>
42945
- <span class="ag-date">{{ agendaDate(group.dateStr) }}</span>
42946
- @if (group.dateStr === todayStr()) { <ui-badge label="Today" variant="blue" /> }
42947
- </div>
42948
- <div class="agenda-events">
42949
- @for (e of group.items; track e.id) {
42950
- <button type="button" class="agenda-event" [attr.aria-label]="e.ariaLabel || null" [attr.title]="e.tooltip || null" (click)="onItemClick(e, $event)">
42951
- <span class="ae-color-bar" [style.background]="e.color"></span>
42952
- <span class="ae-body">
42953
- <span class="ae-top">
42954
- <span class="ae-title" [class.strike]="e.strike">{{ e.title }}</span>
42955
- @for (b of e.badges ?? []; track b.label) { <ui-badge [label]="b.label" [variant]="b.variant" /> }
42956
- </span>
42957
- @if (agendaMeta(e).length) {
42958
- <span class="ae-meta">
42959
- @for (m of agendaMeta(e); track $index) { <span class="ae-meta-item">{{ m }}</span> }
42960
- </span>
42961
- }
42962
- </span>
42963
- </button>
44244
+ @if (agendaSections(); as sections) {
44245
+ @for (s of sections; track $index) {
44246
+ @if (s.label) {
44247
+ <section class="agenda-section" [attr.aria-labelledby]="s.id">
44248
+ <h3 class="agenda-period-hdr" [id]="s.id">{{ s.label }}</h3>
44249
+ @for (group of s.groups; track group.dateStr) {
44250
+ <ng-container *ngTemplateOutlet="agendaGroupTpl; context: { $implicit: group }" />
44251
+ }
44252
+ </section>
44253
+ } @else {
44254
+ @for (group of s.groups; track group.dateStr) {
44255
+ <ng-container *ngTemplateOutlet="agendaGroupTpl; context: { $implicit: group }" />
42964
44256
  }
42965
- </div>
42966
- </div>
44257
+ }
44258
+ }
44259
+ } @else {
44260
+ @for (group of agendaGroups(); track group.dateStr) {
44261
+ <ng-container *ngTemplateOutlet="agendaGroupTpl; context: { $implicit: group }" />
44262
+ }
42967
44263
  }
42968
44264
  </div>
42969
44265
  }
@@ -42972,7 +44268,7 @@ i0.ɵɵngDeclareClassMetadata({
42972
44268
  }
42973
44269
  <ui-toast />
42974
44270
  `,
42975
- styles: [":host{display:flex;flex-direction:column;gap:0;height:100%;min-height:0}.cal-header{background:#fff;border:1px solid #e2e8f0;border-radius:12px;box-shadow:0 1px 2px #0f172a0a;margin-bottom:12px;flex-shrink:0}.cal-toolbar{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;padding:18px 20px;gap:16px;flex-shrink:0}.toolbar-left{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;column-gap:20px;row-gap:8px;min-width:0}.toolbar-center{display:flex;align-items:center;justify-content:center;gap:10px}.toolbar-right{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}.toolbar-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:nowrap}.toolbar-actions:empty{display:none}ui-pill-tabs+.toolbar-actions:not(:empty){margin-left:4px;padding-left:12px;border-left:1px solid #e2e8f0;align-self:stretch}@media(max-width:480px){.toolbar-actions{flex-wrap:wrap}}.period-label{font-size:2.5rem;font-weight:800;line-height:1.15;letter-spacing:-.025em;color:#0f172a;margin:0;min-width:0}.pl-sub{font-weight:600;color:#64748b}.nav-controls{display:inline-flex;align-items:center}.nav-controls>*+*{margin-left:-1px}.btn-nav{width:38px;height:38px;border:1px solid #cbd5e1;background:#fff;font-family:inherit;position:relative;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#475569;transition:background .12s,color .12s}.nav-controls>.btn-nav:first-child{border-radius:10px 0 0 10px}.nav-controls>.btn-nav:last-child{border-radius:0 10px 10px 0}.btn-today{padding:0 18px;height:38px;border:1px solid #cbd5e1;background:#fff;border-radius:0;font-family:inherit;position:relative;font-size:.9375rem;font-weight:600;color:#1d4ed8;cursor:pointer;transition:background .12s}.btn-nav:hover,.btn-today:hover{background:#eff6ff;color:#1d4ed8;z-index:1}.btn-nav:active,.btn-today:active{background:#dbeafe}.btn-nav:focus-visible,.btn-today:focus-visible,.filter-chip:focus-visible,.event-chip:focus-visible,.more-events:focus-visible,.allday-chip:focus-visible,.timed-event:focus-visible,.agenda-event:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.month-cell:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.slot-add:focus,.slot-add:focus-visible{outline:2px solid #3b82f6;outline-offset:1px}.mobile-summary{display:none}.filter-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px 16px;padding:12px 20px 14px;border-top:1px solid #eef2f7;background:#f8fafc;border-radius:0 0 12px 12px;flex-shrink:0}.filter-main{display:flex;flex-direction:column;gap:8px;flex:1 1 auto;min-width:0}.filter-nav{margin-left:auto;flex-shrink:0}.filter-bar-label{font-size:.6875rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.06em}.filter-chips{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.filter-chip{display:inline-flex;align-items:center;gap:5px;padding:4px 12px;border-radius:20px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#475569;cursor:pointer;transition:all .12s;white-space:nowrap;font-family:inherit}.filter-chip:hover{border-color:#cbd5e1;background:#f8fafc}.filter-chip.active-all{background:#1e293b;color:#fff;border-color:#1e293b}.filter-chip.active-all .chip-count{background:#fff3;color:#fff}.filter-chip.active-type{background:var(--chip-bg, #f1f5f9);color:#1e293b;border-color:var(--chip-color);font-weight:600}.filter-chip.active-type .chip-count{background:var(--chip-color);color:#fff}.chip-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}.chip-count{font-size:.6875rem;font-weight:700;background:#f1f5f9;background:color-mix(in srgb,var(--chip-color, #64748b) 14%,#fff);color:#475569;padding:1px 8px;border-radius:999px;min-width:18px;text-align:center;box-sizing:border-box}.cal-skeleton,.outline-grid,.month-view,.week-view,.agenda-view{flex:1;display:flex;flex-direction:column;min-height:0;background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden}.skel-dow-row{display:grid;grid-template-columns:repeat(7,1fr);gap:8px;padding:12px 10px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.skel-dow{display:flex;justify-content:center}.skel-month-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:repeat(6,minmax(80px,1fr));flex:1}.skel-cell{border-right:1px solid #f1f5f9;border-bottom:1px solid #f1f5f9;padding:8px 6px;display:flex;flex-direction:column;gap:6px}.skel-cell:nth-child(7n){border-right:none}.cal-outline{flex:1;display:flex;flex-direction:column;min-height:0}.cal-outline ui-skeleton{display:block;flex-shrink:0}.skel-period-wrap{width:240px;max-width:100%;min-width:0}.skel-nav-controls{gap:2px}.skel-nav-controls>*+*{margin-left:0}.skel-tabs{display:flex;align-items:center;gap:6px}.skel-tabs+.toolbar-actions:not(:empty){margin-left:4px;padding-left:12px;border-left:1px solid #e2e8f0;align-self:stretch}.skel-week-grid{display:grid;grid-template-columns:repeat(7,1fr);flex:1;min-height:360px}.skel-agenda{display:flex;flex-direction:column;flex:1}.skel-row{display:flex;align-items:center;gap:16px;padding:16px 12px;border-bottom:1px solid #f1f5f9}.skel-row-body{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0}.month-grid{display:flex;flex-direction:column;flex:1;min-height:0;overflow:auto}.month-header{display:grid;grid-template-columns:repeat(7,1fr);border-bottom:1px solid #e2e8f0;background:#f8fafc;flex-shrink:0;position:sticky;top:0;z-index:1}.month-dow{padding:10px 8px;font-size:.75rem;font-weight:600;color:#6b7280;text-align:center;text-transform:uppercase;letter-spacing:.04em}.dow-short{display:none}.month-row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));flex:1 0 120px}.month-cell{border-right:1px solid #f1f5f9;border-bottom:1px solid #f1f5f9;padding:6px 6px 4px;min-height:120px;transition:background .1s;min-width:0;box-sizing:border-box}.month-cell.clickable{cursor:pointer}.month-cell.clickable:hover{background:#f8fafc}.month-cell:nth-child(7n){border-right:none}.month-cell.other-month{background:#fafafa}.month-cell.other-month .day-num{color:#64748b}.month-cell.today{background:#eff6ff}.day-num{display:inline-block;width:26px;height:26px;line-height:26px;text-align:center;border-radius:50%;font-size:.8125rem;font-weight:500;color:#374151;margin-bottom:4px}.today-badge{background:#2563eb;color:#fff;font-weight:700}.cell-events{display:flex;flex-direction:column;gap:2px}.event-chip{display:flex;align-items:center;gap:3px;padding:2px 6px;width:100%;box-sizing:border-box;text-align:left;font-family:inherit;border:0;border-radius:4px;border-left:3px solid transparent;font-size:.6875rem;font-weight:500;cursor:pointer;overflow:hidden}.event-chip:hover{opacity:.85}.event-chip.stacked{flex-direction:column;align-items:flex-start;gap:0}.chip-head{display:inline-flex;align-items:center;gap:3px;flex-shrink:0}.chip-icon{flex-shrink:0}.chip-time{font-weight:700;flex-shrink:0;color:#1e293b}.chip-title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#1e293b;min-width:0;max-width:100%}.strike{text-decoration:line-through}.chip-src{font-size:.625rem;font-weight:700;margin-left:auto;flex-shrink:0;color:#1e293b}.more-events{font-size:.6875rem;color:#2563eb;font-weight:600;cursor:pointer;padding:1px 6px;border:0;background:none;text-align:left;font-family:inherit}.more-events:hover{text-decoration:underline}.week-view{overflow:auto}.week-header{display:grid;grid-template-columns:60px repeat(7,1fr);border-bottom:1px solid #e2e8f0;background:#f8fafc;flex-shrink:0;overflow-y:scroll}.time-gutter{border-right:1px solid #e2e8f0}.week-day-hdr{display:flex;flex-direction:column;align-items:center;padding:10px 4px;border-right:1px solid #f1f5f9}.week-day-hdr.today{background:#eff6ff}.week-day-hdr:last-child{border-right:none}.wdh-dow{font-size:.6875rem;font-weight:600;color:#6b7280;text-transform:uppercase}.wdh-num{width:28px;height:28px;line-height:28px;text-align:center;border-radius:50%;font-size:.9375rem;font-weight:600;color:#374151;margin-top:2px}.wdh-num.today-badge{background:#2563eb;color:#fff;font-weight:700}.allday-row{display:grid;grid-template-columns:60px repeat(7,1fr);border-bottom:1px solid #e2e8f0;min-height:32px;flex-shrink:0;overflow-y:scroll}.allday-label{font-size:.625rem;color:#64748b;font-weight:600;text-transform:uppercase;display:flex;align-items:center;justify-content:center}.slot-add{position:absolute;top:2px;left:2px;z-index:3;width:1px;height:1px;padding:0;margin:0;overflow:hidden;clip-path:inset(50%);border:0;white-space:nowrap;font-family:inherit}.slot-add:focus,.slot-add:focus-visible{width:auto;height:auto;padding:2px 8px;clip-path:none;background:#1e293b;color:#fff;border-radius:4px;font-size:.75rem;font-weight:600}.allday-cell{position:relative;border-right:1px solid #f1f5f9;padding:3px 4px;display:flex;flex-direction:column;gap:2px;min-width:0}.allday-cell.clickable{cursor:pointer}.allday-cell:last-child{border-right:none}.allday-chip{padding:2px 6px;border:0;border-radius:4px;border-left:3px solid transparent;font-size:.6875rem;font-weight:500;color:#1e293b;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left;font-family:inherit}.allday-chip:hover{opacity:.85}.week-body{display:grid;grid-template-columns:60px repeat(7,1fr);flex:1;overflow-y:scroll}.time-col{display:flex;flex-direction:column;border-right:1px solid #e2e8f0;flex-shrink:0}.time-slot-label{height:60px;display:flex;align-items:flex-start;justify-content:flex-end;padding:4px 8px 0 0;font-size:.6875rem;color:#64748b;font-weight:500;border-bottom:1px solid #f1f5f9;flex-shrink:0;box-sizing:border-box}.day-col{position:relative;border-right:1px solid #f1f5f9;min-width:0}.day-col.clickable{cursor:pointer}.day-col:last-child{border-right:none}.day-col.today{background:#fafbff}.time-slot{height:60px;border-bottom:1px solid #f1f5f9;box-sizing:border-box}.day-col.clickable .time-slot:hover{background:#f8fafc}.timed-event{position:absolute;min-height:20px;border:0;border-radius:6px;padding:3px 6px;overflow:hidden;cursor:pointer;box-sizing:border-box;border-left:3px solid transparent;display:flex;flex-direction:column;align-items:flex-start;gap:1px;box-shadow:0 1px 3px #00000014;text-align:left;font-family:inherit}.timed-event:hover{opacity:.85}.te-title{font-size:.6875rem;font-weight:600;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.te-time{font-size:.625rem;color:#475569}.agenda-view{overflow-y:auto}.agenda-group{display:flex;border-bottom:1px solid #f1f5f9}.agenda-date-hdr{width:100px;flex-shrink:0;padding:16px 12px;display:flex;flex-direction:column;gap:4px;background:#f8fafc;border-right:2px solid #e2e8f0}.ag-dow{font-size:.6875rem;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.04em}.ag-date{font-size:.8125rem;font-weight:600;color:#374151}.agenda-events{flex:1;display:flex;flex-direction:column;min-width:0}.agenda-event{display:flex;align-items:stretch;padding:10px 12px;cursor:pointer;border:0;border-bottom:1px solid #f8fafc;background:none;text-align:left;width:100%;font-family:inherit;transition:background .1s}.agenda-event:hover{background:#f8fafc}.agenda-event:last-child{border-bottom:none}.ae-color-bar{display:block;width:4px;border-radius:4px;flex-shrink:0;margin-right:12px}.ae-body{flex:1;display:flex;flex-direction:column;gap:4px;min-width:0}.ae-top{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.ae-title{font-size:.9375rem;font-weight:600;color:#111827}.ae-meta{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.ae-meta-item{font-size:.8125rem;color:#6b7280}@media(prefers-reduced-motion:reduce){.btn-nav,.btn-today,.filter-chip,.month-cell,.agenda-event{transition:none}}@media(pointer:coarse),(max-width:480px){.btn-nav,.btn-today,.filter-chip,.event-chip,.more-events,.allday-chip,.agenda-event{min-height:44px}.btn-nav{min-width:44px}}@media(max-width:768px){.cal-toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 12px}.toolbar-left{flex:1 1 auto}.toolbar-right{flex:0 1 auto;min-width:0;max-width:100%}.toolbar-center{display:none}.period-label{font-size:1.875rem}.toolbar-right{justify-content:flex-end}.mobile-summary{display:block;margin-bottom:4px;flex-shrink:0}.mobile-summary:empty{display:none}.filter-chips{flex-wrap:nowrap;overflow-x:auto;padding-bottom:4px;-webkit-overflow-scrolling:touch}.filter-main{flex-basis:100%}.month-view,.cal-skeleton,.outline-grid,.week-view{overflow-x:auto;-webkit-overflow-scrolling:touch}.month-grid,.skel-month-grid,.skel-week-grid,.skel-dow-row{min-width:560px}.month-row{flex-basis:80px}.month-cell{min-height:80px}.week-header,.allday-row,.week-body{min-width:640px}.agenda-date-hdr{width:76px;padding:12px 8px}.ae-title{font-size:.875rem}}@media(max-width:640px){.event-chip.stacked.has-head .chip-title{display:none}}@media(max-width:640px){.cal-toolbar{padding:14px}.period-label{font-size:1.625rem}.filter-bar{padding:10px 14px 12px}.filter-nav{width:100%;margin-left:0}.filter-nav .nav-controls{display:flex;width:100%}.filter-nav .btn-today{flex:1 1 auto}.chip-head{flex-wrap:wrap;row-gap:0;flex-shrink:1;min-width:0}.chip-time{white-space:normal;line-height:1.1;flex-shrink:1;min-width:0;overflow-wrap:anywhere}.event-chip.stacked{flex-wrap:nowrap}.month-view,.cal-skeleton,.outline-grid{overflow-x:hidden}.month-grid,.skel-month-grid,.skel-week-grid,.skel-dow-row{min-width:0}.month-dow{padding:8px 2px;font-size:.6875rem;letter-spacing:0}.month-row{flex-basis:72px}.month-cell{min-height:72px;padding:3px 2px 2px}.day-num{width:22px;height:22px;line-height:22px;font-size:.75rem;margin-bottom:2px}.cell-events{gap:1px}.event-chip{min-height:24px;padding:1px 3px;font-size:.625rem;gap:2px}.more-events{min-height:24px;padding:1px 3px;font-size:.625rem;white-space:normal;line-height:1.1;max-width:100%}}@media(max-width:480px){.dow-full{display:none}.dow-short{display:inline}}\n"]
44271
+ styles: [":host{display:flex;flex-direction:column;gap:0;height:100%;min-height:0}.cal-header{background:#fff;border:1px solid #e2e8f0;border-radius:12px;box-shadow:0 1px 2px #0f172a0a;margin-bottom:12px;flex-shrink:0}.cal-toolbar{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;padding:18px 20px;gap:16px;flex-shrink:0}.toolbar-left{display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;column-gap:20px;row-gap:8px;min-width:0}.toolbar-center{display:flex;align-items:center;justify-content:center;gap:10px}.toolbar-right{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}.toolbar-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:nowrap}.toolbar-actions:empty{display:none}ui-pill-tabs+.toolbar-actions:not(:empty){margin-left:4px;padding-left:12px;border-left:1px solid #e2e8f0;align-self:stretch}@media(max-width:480px){.toolbar-actions{flex-wrap:wrap}}.period-label{font-size:2.5rem;font-weight:800;line-height:1.15;letter-spacing:-.025em;color:#0f172a;margin:0;min-width:0}.pl-sub{font-weight:600;color:#64748b}.nav-controls{display:inline-flex;align-items:center}.nav-controls>*+*{margin-left:-1px}.btn-nav{width:38px;height:38px;border:1px solid #cbd5e1;background:#fff;font-family:inherit;position:relative;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#475569;transition:background .12s,color .12s}.nav-controls>.btn-nav:first-child{border-radius:10px 0 0 10px}.nav-controls>.btn-nav:last-child{border-radius:0 10px 10px 0}.btn-today{padding:0 18px;height:38px;border:1px solid #cbd5e1;background:#fff;border-radius:0;font-family:inherit;position:relative;font-size:.9375rem;font-weight:600;color:#1d4ed8;cursor:pointer;transition:background .12s}.btn-nav:hover,.btn-today:hover{background:#eff6ff;color:#1d4ed8;z-index:1}.btn-nav:active,.btn-today:active{background:#dbeafe}.btn-nav:focus-visible,.btn-today:focus-visible,.filter-chip:focus-visible,.event-chip:focus-visible,.more-events:focus-visible,.allday-chip:focus-visible,.timed-event:focus-visible,.agenda-event:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.month-cell:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.slot-add:focus,.slot-add:focus-visible{outline:2px solid #3b82f6;outline-offset:1px}.mobile-summary{display:none}.filter-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px 16px;padding:12px 20px 14px;border-top:1px solid #eef2f7;background:#f8fafc;border-radius:0 0 12px 12px;flex-shrink:0}.filter-main{display:flex;flex-direction:column;gap:8px;flex:1 1 auto;min-width:0}.filter-nav{margin-left:auto;flex-shrink:0}.filter-bar-label{font-size:.6875rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.06em}.filter-chips{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.filter-chip{display:inline-flex;align-items:center;gap:5px;padding:4px 12px;border-radius:20px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#475569;cursor:pointer;transition:all .12s;white-space:nowrap;font-family:inherit}.filter-chip:hover{border-color:#cbd5e1;background:#f8fafc}.filter-chip.active-all{background:#1e293b;color:#fff;border-color:#1e293b}.filter-chip.active-all .chip-count{background:#fff3;color:#fff}.filter-chip.active-type{background:var(--chip-bg, #f1f5f9);color:#1e293b;border-color:var(--chip-color);font-weight:600}.filter-chip.active-type .chip-count{background:var(--chip-color);color:#fff}.chip-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}.chip-count{font-size:.6875rem;font-weight:700;background:#f1f5f9;background:color-mix(in srgb,var(--chip-color, #64748b) 14%,#fff);color:#475569;padding:1px 8px;border-radius:999px;min-width:18px;text-align:center;box-sizing:border-box}.cal-skeleton,.outline-grid,.month-view,.week-view,.agenda-view{flex:1;display:flex;flex-direction:column;min-height:0;background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden}.skel-dow-row{display:grid;grid-template-columns:repeat(7,1fr);gap:8px;padding:12px 10px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.skel-dow{display:flex;justify-content:center}.skel-month-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:repeat(6,minmax(80px,1fr));flex:1}.skel-cell{border-right:1px solid #f1f5f9;border-bottom:1px solid #f1f5f9;padding:8px 6px;display:flex;flex-direction:column;gap:6px}.skel-cell:nth-child(7n){border-right:none}.cal-outline{flex:1;display:flex;flex-direction:column;min-height:0}.cal-outline ui-skeleton{display:block;flex-shrink:0}.skel-period-wrap{width:240px;max-width:100%;min-width:0}.skel-nav-controls{gap:2px}.skel-nav-controls>*+*{margin-left:0}.skel-tabs{display:flex;align-items:center;gap:6px}.skel-tabs+.toolbar-actions:not(:empty){margin-left:4px;padding-left:12px;border-left:1px solid #e2e8f0;align-self:stretch}.skel-week-grid{display:grid;grid-template-columns:repeat(7,1fr);flex:1;min-height:360px}.skel-agenda{display:flex;flex-direction:column;flex:1}.skel-row{display:flex;align-items:center;gap:16px;padding:16px 12px;border-bottom:1px solid #f1f5f9}.skel-row-body{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0}.month-grid{display:flex;flex-direction:column;flex:1;min-height:0;overflow:auto}.month-header{display:grid;grid-template-columns:repeat(7,1fr);border-bottom:1px solid #e2e8f0;background:#f8fafc;flex-shrink:0;position:sticky;top:0;z-index:1}.month-dow{padding:10px 8px;font-size:.75rem;font-weight:600;color:#6b7280;text-align:center;text-transform:uppercase;letter-spacing:.04em}.dow-short{display:none}.month-row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));flex:1 0 120px}.month-cell{border-right:1px solid #f1f5f9;border-bottom:1px solid #f1f5f9;padding:6px 6px 4px;min-height:120px;transition:background .1s;min-width:0;box-sizing:border-box}.month-cell.clickable{cursor:pointer}.month-cell.clickable:hover{background:#f8fafc}.month-cell:nth-child(7n){border-right:none}.month-cell.other-month{background:#fafafa}.month-cell.other-month .day-num{color:#64748b}.month-cell[data-level=low]{background:#f0fdf4;box-shadow:inset 0 3px #86efac}.month-cell[data-level=medium]{background:#fefce8;box-shadow:inset 0 3px #facc15}.month-cell[data-level=high]{background:#fff7ed;box-shadow:inset 0 3px #fb923c}.month-cell[data-level=critical]{background:#fef2f2;box-shadow:inset 0 3px #ef4444}.month-cell.other-month[data-level] .day-num{color:#475569}.month-cell.today{background:#eff6ff}.day-num{display:inline-block;width:26px;height:26px;line-height:26px;text-align:center;border-radius:50%;font-size:.8125rem;font-weight:500;color:#374151;margin-bottom:4px}.today-badge{background:#2563eb;color:#fff;font-weight:700}.cell-events{display:flex;flex-direction:column;gap:2px}.event-chip{display:flex;align-items:center;gap:3px;padding:2px 6px;width:100%;box-sizing:border-box;text-align:left;font-family:inherit;border:0;border-radius:4px;border-left:3px solid transparent;font-size:.6875rem;font-weight:500;cursor:pointer;overflow:hidden}.event-chip:hover{opacity:.85}.event-chip.stacked{flex-direction:column;align-items:flex-start;gap:0}.chip-head{display:inline-flex;align-items:center;gap:3px;flex-shrink:0}.chip-icon{flex-shrink:0}.chip-time{font-weight:700;flex-shrink:0;color:#1e293b}.chip-title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#1e293b;min-width:0;max-width:100%}.strike{text-decoration:line-through}.chip-src{font-size:.625rem;font-weight:700;margin-left:auto;flex-shrink:0;color:#1e293b}.more-events{font-size:.6875rem;color:#2563eb;font-weight:600;cursor:pointer;padding:1px 6px;border:0;background:none;text-align:left;font-family:inherit}.more-events:hover{text-decoration:underline}.week-view{overflow:auto}.week-header{display:grid;grid-template-columns:60px repeat(7,1fr);border-bottom:1px solid #e2e8f0;background:#f8fafc;flex-shrink:0;overflow-y:scroll}.time-gutter{border-right:1px solid #e2e8f0}.week-day-hdr{display:flex;flex-direction:column;align-items:center;padding:10px 4px;border-right:1px solid #f1f5f9}.week-day-hdr[data-level=low]{background:#f0fdf4;box-shadow:inset 0 3px #86efac}.week-day-hdr[data-level=medium]{background:#fefce8;box-shadow:inset 0 3px #facc15}.week-day-hdr[data-level=high]{background:#fff7ed;box-shadow:inset 0 3px #fb923c}.week-day-hdr[data-level=critical]{background:#fef2f2;box-shadow:inset 0 3px #ef4444}.week-day-hdr.today{background:#eff6ff}.deco-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);border:0;white-space:nowrap}.week-day-hdr:last-child{border-right:none}.wdh-dow{font-size:.6875rem;font-weight:600;color:#6b7280;text-transform:uppercase}.wdh-num{width:28px;height:28px;line-height:28px;text-align:center;border-radius:50%;font-size:.9375rem;font-weight:600;color:#374151;margin-top:2px}.wdh-num.today-badge{background:#2563eb;color:#fff;font-weight:700}.allday-row{display:grid;grid-template-columns:60px repeat(7,1fr);border-bottom:1px solid #e2e8f0;min-height:32px;flex-shrink:0;overflow-y:scroll}.allday-label{font-size:.625rem;color:#64748b;font-weight:600;text-transform:uppercase;display:flex;align-items:center;justify-content:center}.slot-add{position:absolute;top:2px;left:2px;z-index:3;width:1px;height:1px;padding:0;margin:0;overflow:hidden;clip-path:inset(50%);border:0;white-space:nowrap;font-family:inherit}.slot-add:focus,.slot-add:focus-visible{width:auto;height:auto;padding:2px 8px;clip-path:none;background:#1e293b;color:#fff;border-radius:4px;font-size:.75rem;font-weight:600}.allday-cell{position:relative;border-right:1px solid #f1f5f9;padding:3px 4px;display:flex;flex-direction:column;gap:2px;min-width:0}.allday-cell.clickable{cursor:pointer}.allday-cell:last-child{border-right:none}.allday-chip{padding:2px 6px;border:0;border-radius:4px;border-left:3px solid transparent;font-size:.6875rem;font-weight:500;color:#1e293b;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left;font-family:inherit}.allday-chip:hover{opacity:.85}.week-body{display:grid;grid-template-columns:60px repeat(7,1fr);flex:1;overflow-y:scroll}.time-col{display:flex;flex-direction:column;border-right:1px solid #e2e8f0;flex-shrink:0}.time-slot-label{height:60px;display:flex;align-items:flex-start;justify-content:flex-end;padding:4px 8px 0 0;font-size:.6875rem;color:#64748b;font-weight:500;border-bottom:1px solid #f1f5f9;flex-shrink:0;box-sizing:border-box}.day-col{position:relative;border-right:1px solid #f1f5f9;min-width:0}.day-col.clickable{cursor:pointer}.day-col:last-child{border-right:none}.day-col.today{background:#fafbff}.time-slot{height:60px;border-bottom:1px solid #f1f5f9;box-sizing:border-box}.day-col.clickable .time-slot:hover{background:#f8fafc}.timed-event{position:absolute;min-height:20px;border:0;border-radius:6px;padding:3px 6px;overflow:hidden;cursor:pointer;box-sizing:border-box;border-left:3px solid transparent;display:flex;flex-direction:column;align-items:flex-start;gap:1px;box-shadow:0 1px 3px #00000014;text-align:left;font-family:inherit}.timed-event:hover{opacity:.85}.te-title{font-size:.6875rem;font-weight:600;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.te-time{font-size:.625rem;color:#475569}.agenda-view{overflow-y:auto}.agenda-section{display:block}.agenda-period-hdr{margin:0;padding:8px 12px;font-size:.75rem;font-weight:700;color:#475569;text-transform:uppercase;letter-spacing:.06em;background:#f1f5f9;border-bottom:1px solid #e2e8f0;position:-webkit-sticky;position:sticky;top:0;z-index:1}.agenda-group{display:flex;border-bottom:1px solid #f1f5f9}.agenda-date-hdr{width:100px;flex-shrink:0;padding:16px 12px;display:flex;flex-direction:column;gap:4px;background:#f8fafc;border-right:2px solid #e2e8f0}.ag-dow{font-size:.6875rem;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.04em}.ag-date{font-size:.8125rem;font-weight:600;color:#374151}.agenda-events{flex:1;display:flex;flex-direction:column;min-width:0}.agenda-event{display:flex;align-items:stretch;padding:10px 12px;cursor:pointer;border:0;border-bottom:1px solid #f8fafc;background:none;text-align:left;width:100%;font-family:inherit;transition:background .1s}.agenda-event:hover{background:#f8fafc}.agenda-event:last-child{border-bottom:none}.ae-color-bar{display:block;width:4px;border-radius:4px;flex-shrink:0;margin-right:12px}.ae-body{flex:1;display:flex;flex-direction:column;gap:4px;min-width:0}.ae-top{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.ae-title{font-size:.9375rem;font-weight:600;color:#111827}.ae-meta{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.ae-meta-item{font-size:.8125rem;color:#6b7280}.item-wrap{position:relative;display:block;min-width:0}.item-wrap.timed-wrap{position:absolute;min-height:20px}.timed-event.in-wrap{position:static;width:100%;height:100%}.item-actions{position:absolute;top:50%;right:2px;transform:translateY(-50%);display:inline-flex;gap:2px;opacity:0;pointer-events:none;z-index:2}.item-wrap:hover>.item-actions,.item-wrap:focus-within>.item-actions{opacity:1;pointer-events:auto}.agenda-wrap>.item-actions{right:12px}.item-action{min-width:20px;height:20px;padding:0 4px;display:inline-flex;align-items:center;justify-content:center;font-family:inherit;font-size:.6875rem;font-weight:600;color:#334155;background:#fff;border:1px solid #cbd5e1;border-radius:4px;cursor:pointer}.item-action:hover:not(:disabled){background:#eff6ff;color:#1d4ed8}.item-action--danger{color:#b91c1c;border-color:#fecaca}.item-action--danger:hover:not(:disabled){background:#fef2f2;color:#991b1b}.item-action:disabled{opacity:.5;cursor:not-allowed}.item-action:focus-visible{outline:2px solid #3b82f6;outline-offset:1px}@media(hover:none){.agenda-wrap>.item-actions{opacity:1;pointer-events:auto}}@media(pointer:coarse),(max-width:480px){.item-action{min-width:44px;min-height:44px}}@media(prefers-reduced-motion:reduce){.btn-nav,.btn-today,.filter-chip,.month-cell,.agenda-event{transition:none}}@media(pointer:coarse),(max-width:480px){.btn-nav,.btn-today,.filter-chip,.event-chip,.more-events,.allday-chip,.agenda-event{min-height:44px}.btn-nav{min-width:44px}}@media(max-width:768px){.cal-toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 12px}.toolbar-left{flex:1 1 auto}.toolbar-right{flex:0 1 auto;min-width:0;max-width:100%}.toolbar-center{display:none}.period-label{font-size:1.875rem}.toolbar-right{justify-content:flex-end}.mobile-summary{display:block;margin-bottom:4px;flex-shrink:0}.mobile-summary:empty{display:none}.filter-chips{flex-wrap:nowrap;overflow-x:auto;padding-bottom:4px;-webkit-overflow-scrolling:touch}.filter-main{flex-basis:100%}.month-view,.cal-skeleton,.outline-grid,.week-view{overflow-x:auto;-webkit-overflow-scrolling:touch}.month-grid,.skel-month-grid,.skel-week-grid,.skel-dow-row{min-width:560px}.month-row{flex-basis:80px}.month-cell{min-height:80px}.week-header,.allday-row,.week-body{min-width:640px}.agenda-date-hdr{width:76px;padding:12px 8px}.ae-title{font-size:.875rem}}@media(max-width:640px){.event-chip.stacked.has-head .chip-title{display:none}}@media(max-width:640px){.cal-toolbar{padding:14px}.period-label{font-size:1.625rem}.filter-bar{padding:10px 14px 12px}.filter-nav{width:100%;margin-left:0}.filter-nav .nav-controls{display:flex;width:100%}.filter-nav .btn-today{flex:1 1 auto}.chip-head{flex-wrap:wrap;row-gap:0;flex-shrink:1;min-width:0}.chip-time{white-space:normal;line-height:1.1;flex-shrink:1;min-width:0;overflow-wrap:anywhere}.event-chip.stacked{flex-wrap:nowrap}.month-view,.cal-skeleton,.outline-grid{overflow-x:hidden}.month-grid,.skel-month-grid,.skel-week-grid,.skel-dow-row{min-width:0}.month-dow{padding:8px 2px;font-size:.6875rem;letter-spacing:0}.month-row{flex-basis:72px}.month-cell{min-height:72px;padding:3px 2px 2px}.day-num{width:22px;height:22px;line-height:22px;font-size:.75rem;margin-bottom:2px}.cell-events{gap:1px}.event-chip{min-height:24px;padding:1px 3px;font-size:.625rem;gap:2px}.more-events{min-height:24px;padding:1px 3px;font-size:.625rem;white-space:normal;line-height:1.1;max-width:100%}}@media(max-width:480px){.dow-full{display:none}.dow-short{display:inline}}\n"]
42976
44272
  }]
42977
44273
  }],
42978
44274
  ctorParameters: () => [],
@@ -43154,6 +44450,30 @@ i0.ɵɵngDeclareClassMetadata({
43154
44450
  required: false
43155
44451
  }]
43156
44452
  }],
44453
+ dayDecorationFn: [{
44454
+ type: i0.Input,
44455
+ args: [{
44456
+ isSignal: true,
44457
+ alias: "dayDecorationFn",
44458
+ required: false
44459
+ }]
44460
+ }],
44461
+ itemActionsFn: [{
44462
+ type: i0.Input,
44463
+ args: [{
44464
+ isSignal: true,
44465
+ alias: "itemActionsFn",
44466
+ required: false
44467
+ }]
44468
+ }],
44469
+ agendaGroupFn: [{
44470
+ type: i0.Input,
44471
+ args: [{
44472
+ isSignal: true,
44473
+ alias: "agendaGroupFn",
44474
+ required: false
44475
+ }]
44476
+ }],
43157
44477
  rangeChange: [{
43158
44478
  type: i0.Output,
43159
44479
  args: ["rangeChange"]
@@ -43173,6 +44493,10 @@ i0.ɵɵngDeclareClassMetadata({
43173
44493
  moreClick: [{
43174
44494
  type: i0.Output,
43175
44495
  args: ["moreClick"]
44496
+ }],
44497
+ itemAction: [{
44498
+ type: i0.Output,
44499
+ args: ["itemAction"]
43176
44500
  }]
43177
44501
  }
43178
44502
  });
@@ -43750,6 +45074,50 @@ function formatAddress(parts, options = {}) {
43750
45074
  zip
43751
45075
  ].filter(Boolean).join(separator);
43752
45076
  }
45077
+ function getInitials(firstName, lastName) {
45078
+ return (firstName?.[0] || "") + (lastName?.[0] || "");
45079
+ }
45080
+ function formatDuration(seconds) {
45081
+ if (!seconds) return "";
45082
+ const m = Math.floor(seconds / 60);
45083
+ const s = seconds % 60;
45084
+ return m > 0 ? `${m}m ${s}s` : `${s}s`;
45085
+ }
45086
+ function formatPhone(raw) {
45087
+ if (!raw) return "";
45088
+ const normalized = raw.startsWith("+1") && raw.replace(/\D/g, "").length === 11 ? raw.slice(2) : raw;
45089
+ if (normalized.startsWith("+")) return normalized;
45090
+ const digits = normalized.replace(/\D/g, "");
45091
+ const d = digits.startsWith("1") && digits.length === 11 ? digits.slice(1) : digits.slice(0, 10);
45092
+ if (d.length <= 3) return d;
45093
+ if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;
45094
+ return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;
45095
+ }
45096
+ function formatPhoneDisplay(e164) {
45097
+ const value = (e164 ?? "").trim();
45098
+ const m = /^\+1(\d{3})(\d{3})(\d{4})$/.exec(value);
45099
+ return m ? `+1 ${m[1]} ${m[2]}-${m[3]}` : e164 ?? "";
45100
+ }
45101
+ function formatShortDate(value) {
45102
+ if (!value) return "—";
45103
+ return new Date(/^\d{4}-\d{2}-\d{2}$/.test(value) ? value + "T00:00:00" : value).toLocaleDateString("en-US", {
45104
+ month: "short",
45105
+ day: "numeric",
45106
+ year: "numeric"
45107
+ });
45108
+ }
45109
+ function todayInTimezone(timezone) {
45110
+ const fmt = (tz) => new Intl.DateTimeFormat("en-CA", {
45111
+ timeZone: tz,
45112
+ year: "numeric",
45113
+ month: "2-digit",
45114
+ day: "2-digit"
45115
+ }).format(/* @__PURE__ */ new Date());
45116
+ if (timezone) try {
45117
+ return fmt(timezone);
45118
+ } catch {}
45119
+ return fmt();
45120
+ }
43753
45121
  function downloadBlob(blob, fileName) {
43754
45122
  if (typeof document === "undefined" || typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return;
43755
45123
  const url = URL.createObjectURL(blob);
@@ -50160,7 +51528,16 @@ var ShareMenuComponent = class ShareMenuComponent {
50160
51528
  kind: "component",
50161
51529
  type: ToastComponent,
50162
51530
  selector: "ui-toast",
50163
- inputs: ["dismissible", "persistentLiveRegion"]
51531
+ inputs: [
51532
+ "dismissible",
51533
+ "persistentLiveRegion",
51534
+ "queue",
51535
+ "countdown",
51536
+ "maxQueued",
51537
+ "countdownLabel",
51538
+ "queueLabel"
51539
+ ],
51540
+ outputs: ["action"]
50164
51541
  }],
50165
51542
  changeDetection: i0.ChangeDetectionStrategy.OnPush
50166
51543
  });
@@ -53557,6 +54934,6 @@ i0.ɵɵngDeclareClassMetadata({
53557
54934
  }]
53558
54935
  }
53559
54936
  });
53560
- export { ActionBarComponent, AlertComponent, AppShellComponent, AudioPlayerComponent, AvatarComponent, BADGE_VARIANT_STYLES, BadgeComponent, BankAccountFormComponent, BarRowComponent, BoardCardDirective, BoardComponent, BulkMessagePanelComponent, BusinessHoursComponent, ButtonComponent, CalendarComponent, CardListItemComponent, CheckboxComponent, CheckboxTrailingDirective, ChipComponent, ChipGroupComponent, ColorInputComponent, ColumnFilterComponent, ColumnsComponent, ComboboxComponent, ConfirmOutletComponent, ConfirmService, ContactPickerComponent, ConversationRichBodyComponent, ConversationsComponent, DEFAULT_BANK_ACCOUNT_TYPE_OPTIONS, DEFAULT_BANK_HOLDER_TYPE_OPTIONS, DEFAULT_BANK_MANDATE_TEXT, DEFAULT_EMAIL_TEMPLATE_BLOCK_GROUPS, DEFAULT_EMAIL_TEMPLATE_VARIABLE_GROUPS, DEFAULT_PASSWORD_RULES, DROPDOWN_GROUP, DataTableComponent, DateInputComponent, DateRangePickerComponent, DatetimeInputComponent, DetailCardComponent, DiscountSelectorComponent, DividerComponent, DropdownComponent, EMAIL_BLOCK_DRAG_TYPE, EMAIL_TEMPLATE_MAX_BODY, EMAIL_TEMPLATE_MAX_TEXT, EMAIL_TEMPLATE_START_TEXT, EmailTemplateEditorComponent, EmptyStateComponent, ErrorComponent, ErrorStateComponent, FORGEROUTE_CONFIG, FadeComponent, FileInputComponent, FillViewportDirective, FixedFooterComponent, FormColSpanDirective, FormComponent, FormRowComponent, FormSectionComponent, HintComponent, IDX_BEHAVIOR_SIGNALS, INTEGRATION_CATALOG, INTEGRATION_FIELD_DEFS, INTEGRATION_GROUP_ORDER, INTEGRATION_OPTION_DEFS, IconComponent, ImageComponent, ImageGalleryComponent, ImageGalleryDialogComponent, ImagePreviewComponent, ImageRepositionComponent, ImportExportComponent, ImportSourceGridComponent, KeypadComponent, KpiCardComponent, LAYOUT_GAP_PX, ListComponent, ListItemComponent, LiveRegionComponent, MAX_EMAIL_ADDRESS_LENGTH, ModalComponent, MoreMenuComponent, MultiSelectComponent, NOTES_MAX_LENGTH, NotesComponent, OfflineBannerComponent, OptionCardComponent, PHONE_NUMBER_AREA_CODE_LENGTH, PHONE_NUMBER_ZIP_LENGTH, PageComponent, PaginationComponent, PanelInfoListComponent, PasswordRequirementsComponent, PdfHotspotComponent, PhoneNumberPickerComponent, PillTabsComponent, ProgressBarComponent, ProgressRingComponent, QrCodeComponent, QuickFilterTabsComponent, RICH_TEXT_OPTIONS, RadioComponent, RadioGroupComponent, RadioMatrixComponent, RadioOptionContentDirective, ReadonlyFieldsetComponent, ReminderAlertsComponent, RichTextEditorComponent, SEMANTIC_BADGE_MAP, SETUP_DONE_KEY, SETUP_INTEGRATIONS, SearchInputComponent, SectionHeaderComponent, SelectComponent, SetupWizardComponent, SetupWizardStepDirective, SetupWizardStepHeaderDirective, ShareMenuComponent, ShowComponent, SidePanelComponent, SidebarLayoutComponent, SignaturePadComponent, SkeletonComponent, SnackbarComponent, SnackbarService, StackComponent, StarComponent, StepperComponent, TableToolbarComponent, TextComponent, TextInputComponent, TextTemplatePickerComponent, TextareaInputComponent, TimeInputComponent, TimelineComponent, ToastComponent, ToastService, ToggleBadgeComponent, ToggleComponent, TooltipDirective, US_STATES, UiFieldValidity, acceptLabel, appShellGroupsStorageKey, autoVariant, blockHasContent, buildDefaultSchedule, buildPreviewDocument, buildVideoBlockHtml, builtInActionIcon, businessTimeOptions, conversationDateLabel, conversationHtmlToText, conversationInitial, conversationMessageStatusLabel, conversationMessageStatusMark, createFieldReveal, decodeFilterValues, digitsOnly, downloadBlob, emailShareUrl, encodeFilterValue, encodeFilterValues, errorContextInterceptor, escapeHtml, facebookShareUrl, fileMatchesAccept, formatAddress, formatCategoryLabel, formatConversationPhone, formatConversationTime, formatCurrency, formatFileSize, formatIsoDate, formatIsoDateTime, formatMessageTime, formatTime, formatVideoDuration, getEligibleIds, getEligibleOptions, getErrorMessage, gmailShareUrl, groupMessagesByDay, humanReadableMessage, iconPath, insideEditorBlock, integrationPlainText, integrationsForApp, integrationsOnlyFor, isBlockDrag, isValidEmailAddress, isWebUrl, isoKey, issueToErrors, linkedInShareUrl, maxLengthIssue, mergeDescribedBy, normalizeHexColor, outlookShareUrl, parseBizHours, parseIso, parseTime, passwordMeetsRules, pluralize, provideForgerouteToast, reorderItems, requiredIssue, resolveLayoutGap, resolvePreset, serializeBizHours, serializedTextLength, setupIntegrations, splitIntro, truncateConversationText, xShareUrl };
54937
+ export { ASCII_SPECIAL_PASSWORD_RULES, ActionBarComponent, AlertComponent, AppShellComponent, AudioPlayerComponent, AvatarComponent, BADGE_VARIANT_STYLES, BadgeComponent, BankAccountFormComponent, BarRowComponent, BoardCardDirective, BoardComponent, BulkMessagePanelComponent, BusinessHoursComponent, ButtonComponent, COMMON_DETAIL_GROUP_STYLES, CalendarComponent, CardListItemComponent, CheckboxComponent, CheckboxTrailingDirective, ChipComponent, ChipGroupComponent, ColorInputComponent, ColumnFilterComponent, ColumnsComponent, ComboboxComponent, ConfirmOutletComponent, ConfirmService, ContactPickerComponent, ConversationRichBodyComponent, ConversationsComponent, DEFAULT_BANK_ACCOUNT_TYPE_OPTIONS, DEFAULT_BANK_HOLDER_TYPE_OPTIONS, DEFAULT_BANK_MANDATE_TEXT, DEFAULT_EMAIL_TEMPLATE_BLOCK_GROUPS, DEFAULT_EMAIL_TEMPLATE_VARIABLE_GROUPS, DEFAULT_PASSWORD_RULES, DETAIL_CARD_HEADER_BG, DETAIL_GROUP_HEADER_BG, DROPDOWN_GROUP, DataTableComponent, DateInputComponent, DateRangePickerComponent, DatetimeInputComponent, DetailCardComponent, DiscountSelectorComponent, DividerComponent, DropdownComponent, EMAIL_BLOCK_DRAG_TYPE, EMAIL_TEMPLATE_MAX_BODY, EMAIL_TEMPLATE_MAX_TEXT, EMAIL_TEMPLATE_START_TEXT, EmailTemplateEditorComponent, EmptyStateComponent, ErrorComponent, ErrorStateComponent, FORGEROUTE_CONFIG, FadeComponent, FileInputComponent, FillViewportDirective, FixedFooterComponent, FormColSpanDirective, FormComponent, FormRowComponent, FormSectionComponent, HintComponent, IDX_BEHAVIOR_SIGNALS, INTEGRATION_CATALOG, INTEGRATION_FIELD_DEFS, INTEGRATION_GROUP_ORDER, INTEGRATION_OPTION_DEFS, IconComponent, ImageComponent, ImageGalleryComponent, ImageGalleryDialogComponent, ImagePreviewComponent, ImageRepositionComponent, ImportExportComponent, ImportSourceGridComponent, KeypadComponent, KpiCardComponent, LAYOUT_GAP_PX, LISTED_SPECIAL_PASSWORD_RULES, ListComponent, ListItemComponent, LiveRegionComponent, MAX_EMAIL_ADDRESS_LENGTH, ModalComponent, MoreMenuComponent, MultiSelectComponent, NOTES_MAX_LENGTH, NotesComponent, OfflineBannerComponent, OptionCardComponent, PASSWORD_MAX_LENGTH, PHONE_NUMBER_AREA_CODE_LENGTH, PHONE_NUMBER_ZIP_LENGTH, PageComponent, PaginationComponent, PanelInfoListComponent, PasswordRequirementsComponent, PdfHotspotComponent, PhoneNumberPickerComponent, PillTabsComponent, ProgressBarComponent, ProgressRingComponent, QrCodeComponent, QuickFilterTabsComponent, RICH_TEXT_OPTIONS, RadioComponent, RadioGroupComponent, RadioMatrixComponent, RadioOptionContentDirective, ReadonlyFieldsetComponent, ReminderAlertsComponent, RichTextEditorComponent, SEMANTIC_BADGE_MAP, SETUP_DONE_KEY, SETUP_INTEGRATIONS, SearchInputComponent, SectionHeaderComponent, SelectComponent, SetupWizardComponent, SetupWizardStepDirective, SetupWizardStepHeaderDirective, ShareMenuComponent, ShowComponent, SidePanelComponent, SidebarLayoutComponent, SignaturePadComponent, SkeletonComponent, SnackbarComponent, SnackbarService, StackComponent, StarComponent, StepperComponent, TableToolbarComponent, TextComponent, TextInputComponent, TextTemplatePickerComponent, TextareaInputComponent, TimeInputComponent, TimelineComponent, ToastComponent, ToastService, ToggleBadgeComponent, ToggleComponent, TooltipDirective, US_LOCATIONS, US_STATES, UiFieldValidity, acceptLabel, appShellGroupsStorageKey, autoVariant, blockHasContent, buildDefaultSchedule, buildPreviewDocument, buildVideoBlockHtml, builtInActionIcon, businessTimeOptions, conversationDateLabel, conversationHtmlToText, conversationInitial, conversationMessageStatusLabel, conversationMessageStatusMark, createFieldReveal, createPasswordPolicyValidator, decodeFilterValues, digitsOnly, downloadBlob, emailShareUrl, encodeFilterValue, encodeFilterValues, errorContextInterceptor, escapeHtml, facebookShareUrl, fileMatchesAccept, formatAddress, formatCategoryLabel, formatConversationPhone, formatConversationTime, formatCurrency, formatDuration, formatFileSize, formatIsoDate, formatIsoDateTime, formatMessageTime, formatPhone, formatPhoneDisplay, formatShortDate, formatTime, formatVideoDuration, getEligibleIds, getEligibleOptions, getErrorMessage, getInitials, gmailShareUrl, groupMessagesByDay, humanReadableMessage, iconPath, insideEditorBlock, integrationPlainText, integrationsForApp, integrationsOnlyFor, isBlockDrag, isValidEmailAddress, isWebUrl, isoKey, issueToErrors, linkedInShareUrl, maxLengthIssue, mergeDescribedBy, normalizeHexColor, outlookShareUrl, parseBizHours, parseIso, parseTime, passwordMeetsRules, periodGroup, pluralize, provideForgerouteToast, reorderItems, requiredIssue, resolveLayoutGap, resolvePreset, serializeBizHours, serializedTextLength, setupIntegrations, splitIntro, todayInTimezone, truncateConversationText, xShareUrl };
53561
54938
 
53562
54939
  //# sourceMappingURL=forgeroute-ui.mjs.map