@lieselthuriot/widget-renderer 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { a as dutchWidgetRendererLocale, i as defaultWidgetRendererLocale, n as chartAriaLabel, o as frenchWidgetRendererLocale, r as WidgetTooltip, s as germanWidgetRendererLocale, t as chartAccessibilitySummary } from "./lieselthuriot-widget-renderer-chart-accessibility-CXEeXwdH.mjs";
1
+ import { a as dutchWidgetRendererLocale, i as defaultWidgetRendererLocale, n as chartAriaLabel, o as frenchWidgetRendererLocale, r as WidgetTooltip, s as germanWidgetRendererLocale, t as chartAccessibilitySummary } from "./lieselthuriot-widget-renderer-chart-accessibility-BzH2alxy.mjs";
2
2
  import * as i0 from "@angular/core";
3
3
  import { Component, computed, effect, inject, input, output, signal, viewChild } from "@angular/core";
4
4
  function normalizeElement(element) {
@@ -400,18 +400,40 @@ function validateWidget(value) {
400
400
  else value["elements"].forEach((element, index) => validateElement(element, `elements[${index}]`, issues));
401
401
  return issues;
402
402
  }
403
- function safeParseWidgetInput(value) {
404
- const issues = validateWidget(value);
405
- return issues.length ? {
403
+ function runRule(rule, widget) {
404
+ const mode = typeof rule === "function" ? "block" : rule.mode ?? "block";
405
+ try {
406
+ return (typeof rule === "function" ? rule(widget) : rule.validate(widget)).map((issue) => ({
407
+ ...issue,
408
+ severity: mode
409
+ }));
410
+ } catch (error) {
411
+ return [{
412
+ path: "",
413
+ message: `Validation rule failed: ${error instanceof Error ? error.message : String(error)}`,
414
+ severity: mode
415
+ }];
416
+ }
417
+ }
418
+ function safeParseWidgetInput(value, options = {}) {
419
+ const builtInIssues = validateWidget(value);
420
+ if (builtInIssues.length) return {
421
+ success: false,
422
+ issues: builtInIssues
423
+ };
424
+ const data = value;
425
+ const issues = (options.rules ?? []).flatMap((rule) => runRule(rule, data));
426
+ return issues.some((issue) => issue.severity === "block") ? {
406
427
  success: false,
407
428
  issues
408
429
  } : {
409
430
  success: true,
410
- data: value
431
+ data,
432
+ issues
411
433
  };
412
434
  }
413
- function parseWidgetInput(value) {
414
- const result = safeParseWidgetInput(value);
435
+ function parseWidgetInput(value, options = {}) {
436
+ const result = safeParseWidgetInput(value, options);
415
437
  if (!result.success) throw new WidgetValidationError(result.issues);
416
438
  return result.data;
417
439
  }
@@ -421,6 +443,232 @@ function isWidgetContract(value) {
421
443
  function isWidget(value) {
422
444
  return safeParseWidgetInput(value).success;
423
445
  }
446
+ const WIDGET_FINGERPRINT_VERSION = 1;
447
+ function computeWidgetFingerprint(input) {
448
+ const result = safeParseWidgetInput(input);
449
+ if (!result.success) return null;
450
+ const canonical = canonicalJson(widgetShape(result.data));
451
+ return `wfp:1:${sha256Hex(new TextEncoder().encode(canonical))}`;
452
+ }
453
+ function widgetShape(widget) {
454
+ return {
455
+ version: widget.version,
456
+ size: widget.size,
457
+ rows: widget.rows,
458
+ columns: widget.columns,
459
+ background: widget.background,
460
+ refresh: { enabled: widget.refresh?.enabled ?? false },
461
+ elements: widget.elements.map(elementShape)
462
+ };
463
+ }
464
+ function hasContent(value) {
465
+ return (value ?? "").trim().length > 0;
466
+ }
467
+ function elementShape(element) {
468
+ const base = {
469
+ type: element.type,
470
+ grid: element.grid
471
+ };
472
+ switch (element.type) {
473
+ case "text": return {
474
+ ...base,
475
+ sensitive: element.sensitive,
476
+ hasText: hasContent(element.text),
477
+ style: {
478
+ ...element.style,
479
+ font: {
480
+ size: element.style.font.size,
481
+ bold: element.style.font.bold ?? false,
482
+ italic: element.style.font.italic ?? false,
483
+ underline: element.style.font.underline ?? false,
484
+ strikethrough: element.style.font.strikethrough ?? false
485
+ },
486
+ padding: element.style.padding ?? {
487
+ vertical: 0,
488
+ horizontal: 0
489
+ }
490
+ }
491
+ };
492
+ case "image": return {
493
+ ...base,
494
+ sensitive: element.sensitive,
495
+ source: element.source,
496
+ hasAlt: hasContent(element.alt),
497
+ fit: element.fit,
498
+ overlayColor: element.overlayColor,
499
+ radius: element.radius
500
+ };
501
+ case "button": return {
502
+ ...base,
503
+ sensitive: element.sensitive,
504
+ hasLabel: hasContent(element.label),
505
+ method: element.method,
506
+ requestRefresh: element.requestRefresh ?? false,
507
+ showOnlyOnHover: element.showOnlyOnHover ?? false,
508
+ style: element.style
509
+ };
510
+ case "divider": return {
511
+ ...base,
512
+ orientation: element.orientation,
513
+ style: element.style
514
+ };
515
+ default: return {
516
+ ...base,
517
+ sensitive: element.sensitive,
518
+ hasTitle: hasContent(element.title),
519
+ showLegend: element.showLegend,
520
+ style: element.style
521
+ };
522
+ }
523
+ }
524
+ function canonicalJson(value) {
525
+ if (value === null || value === void 0) return "null";
526
+ if (typeof value === "boolean") return value ? "true" : "false";
527
+ if (typeof value === "number") return canonicalNumber(value);
528
+ if (typeof value === "string") return canonicalString(value);
529
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
530
+ return `{${Object.keys(value).filter((key) => value[key] !== void 0 && value[key] !== null).sort(compareOrdinal).map((key) => `${canonicalString(key)}:${canonicalJson(value[key])}`).join(",")}}`;
531
+ }
532
+ function compareOrdinal(left, right) {
533
+ return left < right ? -1 : left > right ? 1 : 0;
534
+ }
535
+ function canonicalNumber(value) {
536
+ const fixed = value.toFixed(6).replace(/\.?0+$/, "");
537
+ return fixed === "-0" ? "0" : fixed;
538
+ }
539
+ function canonicalString(value) {
540
+ const normalized = value.startsWith("#") ? value.toLowerCase() : value;
541
+ let output = "\"";
542
+ for (const character of normalized) {
543
+ const code = character.charCodeAt(0);
544
+ if (character === "\"" || character === "\\") output += `\\${character}`;
545
+ else if (code < 32) output += `\\u${code.toString(16).padStart(4, "0")}`;
546
+ else output += character;
547
+ }
548
+ return `${output}"`;
549
+ }
550
+ const roundConstants = new Uint32Array([
551
+ 1116352408,
552
+ 1899447441,
553
+ 3049323471,
554
+ 3921009573,
555
+ 961987163,
556
+ 1508970993,
557
+ 2453635748,
558
+ 2870763221,
559
+ 3624381080,
560
+ 310598401,
561
+ 607225278,
562
+ 1426881987,
563
+ 1925078388,
564
+ 2162078206,
565
+ 2614888103,
566
+ 3248222580,
567
+ 3835390401,
568
+ 4022224774,
569
+ 264347078,
570
+ 604807628,
571
+ 770255983,
572
+ 1249150122,
573
+ 1555081692,
574
+ 1996064986,
575
+ 2554220882,
576
+ 2821834349,
577
+ 2952996808,
578
+ 3210313671,
579
+ 3336571891,
580
+ 3584528711,
581
+ 113926993,
582
+ 338241895,
583
+ 666307205,
584
+ 773529912,
585
+ 1294757372,
586
+ 1396182291,
587
+ 1695183700,
588
+ 1986661051,
589
+ 2177026350,
590
+ 2456956037,
591
+ 2730485921,
592
+ 2820302411,
593
+ 3259730800,
594
+ 3345764771,
595
+ 3516065817,
596
+ 3600352804,
597
+ 4094571909,
598
+ 275423344,
599
+ 430227734,
600
+ 506948616,
601
+ 659060556,
602
+ 883997877,
603
+ 958139571,
604
+ 1322822218,
605
+ 1537002063,
606
+ 1747873779,
607
+ 1955562222,
608
+ 2024104815,
609
+ 2227730452,
610
+ 2361852424,
611
+ 2428436474,
612
+ 2756734187,
613
+ 3204031479,
614
+ 3329325298
615
+ ]);
616
+ function sha256Hex(message) {
617
+ const bitLength = message.length * 8;
618
+ const paddedLength = Math.ceil((message.length + 9) / 64) * 64;
619
+ const bytes = new Uint8Array(paddedLength);
620
+ bytes.set(message);
621
+ bytes[message.length] = 128;
622
+ const view = new DataView(bytes.buffer);
623
+ view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
624
+ view.setUint32(paddedLength - 4, bitLength >>> 0);
625
+ const hash = new Uint32Array([
626
+ 1779033703,
627
+ 3144134277,
628
+ 1013904242,
629
+ 2773480762,
630
+ 1359893119,
631
+ 2600822924,
632
+ 528734635,
633
+ 1541459225
634
+ ]);
635
+ const words = /* @__PURE__ */ new Uint32Array(64);
636
+ const rotate = (value, bits) => value >>> bits | value << 32 - bits;
637
+ for (let offset = 0; offset < paddedLength; offset += 64) {
638
+ for (let index = 0; index < 16; index++) words[index] = view.getUint32(offset + index * 4);
639
+ for (let index = 16; index < 64; index++) {
640
+ const w15 = words[index - 15];
641
+ const w2 = words[index - 2];
642
+ const s0 = rotate(w15, 7) ^ rotate(w15, 18) ^ w15 >>> 3;
643
+ const s1 = rotate(w2, 17) ^ rotate(w2, 19) ^ w2 >>> 10;
644
+ words[index] = words[index - 16] + s0 + words[index - 7] + s1;
645
+ }
646
+ let [a, b, c, d, e, f, g, h] = hash;
647
+ for (let index = 0; index < 64; index++) {
648
+ const s1 = rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25);
649
+ const choice = e & f ^ ~e & g;
650
+ const temp1 = h + s1 + choice + roundConstants[index] + words[index] >>> 0;
651
+ const temp2 = (rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22)) + (a & b ^ a & c ^ b & c) >>> 0;
652
+ h = g;
653
+ g = f;
654
+ f = e;
655
+ e = d + temp1 >>> 0;
656
+ d = c;
657
+ c = b;
658
+ b = a;
659
+ a = temp1 + temp2 >>> 0;
660
+ }
661
+ hash[0] += a;
662
+ hash[1] += b;
663
+ hash[2] += c;
664
+ hash[3] += d;
665
+ hash[4] += e;
666
+ hash[5] += f;
667
+ hash[6] += g;
668
+ hash[7] += h;
669
+ }
670
+ return Array.from(hash, (word) => word.toString(16).padStart(8, "0")).join("");
671
+ }
424
672
  const WIDGET_HEIGHT = 245;
425
673
  const WIDGET_PADDING = 18;
426
674
  const WIDGET_GAP = 8;
@@ -561,7 +809,7 @@ var WidgetElementRenderer = class WidgetElementRenderer {
561
809
  }
562
810
  static ɵfac = i0.ɵɵngDeclareFactory({
563
811
  minVersion: "12.0.0",
564
- version: "22.2.3",
812
+ version: "22.2.4",
565
813
  ngImport: i0,
566
814
  type: WidgetElementRenderer,
567
815
  deps: [],
@@ -569,7 +817,7 @@ var WidgetElementRenderer = class WidgetElementRenderer {
569
817
  });
570
818
  static ɵcmp = i0.ɵɵngDeclareComponent({
571
819
  minVersion: "17.0.0",
572
- version: "22.2.3",
820
+ version: "22.2.4",
573
821
  type: WidgetElementRenderer,
574
822
  isStandalone: true,
575
823
  selector: "widget-element-renderer",
@@ -604,15 +852,15 @@ var WidgetElementRenderer = class WidgetElementRenderer {
604
852
  ngImport: i0,
605
853
  template: "@let item = element();\n<div\n class=\"widget-element\"\n [attr.data-widget-element-id]=\"elementId(item)\"\n [attr.aria-hidden]=\"isSensitive(item) ? 'true' : null\"\n [class.is-sensitive]=\"isSensitive(item)\"\n>\n <div\n class=\"element-content\"\n [class.is-sensitive]=\"isSensitive(item)\"\n [style.--sensitive-blur]=\"sensitiveBlur(item)\"\n [style.justify-content]=\"\n isChart(item) && item.type !== 'progress'\n ? 'stretch'\n : horizontalAnchor(item.grid.anchor ?? 'center')\n \"\n [style.align-items]=\"\n isChart(item) && item.type !== 'progress'\n ? 'stretch'\n : verticalAnchor(item.grid.anchor ?? 'center')\n \"\n >\n @if (isText(item)) {\n @let text = item;\n <span\n class=\"text-element\"\n [style.color]=\"text.style.color\"\n [style.font-size.px]=\"text.style.font.size\"\n [style.font-weight]=\"textFontWeight(text)\"\n [style.font-style]=\"textFontStyle(text)\"\n [style.text-decoration]=\"textFontDecoration(text)\"\n [style.text-align]=\"text.style.align\"\n [style.white-space]=\"text.style.wrap ? 'pre-wrap' : 'pre'\"\n [style.background-color]=\"text.style.background || null\"\n [style.border-radius.px]=\"text.style.radius\"\n [style.padding]=\"\n text.style.padding\n ? text.style.padding.vertical + 'px ' + text.style.padding.horizontal + 'px'\n : null\n \"\n >{{ text.text }}</span\n >\n } @else if (isImage(item)) {\n @let image = item;\n @if (imageFailed(image)) {\n <div\n class=\"image-placeholder\"\n role=\"img\"\n [attr.aria-label]=\"elementAlt(image) || locale().aria.imageUnavailable\"\n [style.border-radius.px]=\"image.radius\"\n >\n <svg aria-hidden=\"true\" viewBox=\"0 0 64 48\" focusable=\"false\">\n <rect x=\"2\" y=\"2\" width=\"60\" height=\"44\" rx=\"5\" />\n <circle cx=\"20\" cy=\"17\" r=\"5\" />\n <path d=\"m8 39 15-14 9 8 7-6 17 12\" />\n </svg>\n </div>\n } @else {\n <img\n [src]=\"image.source\"\n [alt]=\"elementAlt(image)\"\n [style.object-fit]=\"image.fit\"\n [style.object-position]=\"image.grid.anchor ?? 'center'\"\n [style.border-radius.px]=\"image.radius\"\n (error)=\"imageLoadFailed(image)\"\n />\n }\n <span\n class=\"image-overlay\"\n [style.background-color]=\"image.overlayColor || 'transparent'\"\n [style.border-radius.px]=\"image.radius\"\n ></span>\n } @else if (isButton(item)) {\n @let button = item;\n <button\n type=\"button\"\n class=\"action-button\"\n [class.solid]=\"button.style === 'solid'\"\n [class.outline]=\"button.style === 'outline'\"\n [class.show-only-on-hover]=\"button.showOnlyOnHover\"\n [disabled]=\"isSensitive(button)\"\n (click)=\"requestAction(button, $event)\"\n [tabindex]=\"isSensitive(button) ? -1 : 0\"\n >\n {{ button.label }}\n </button>\n } @else if (item.type === 'divider') {\n @let divider = item;\n <div\n class=\"divider-element\"\n [class.is-vertical]=\"divider.orientation === 'vertical'\"\n role=\"separator\"\n [attr.aria-orientation]=\"divider.orientation\"\n [style.background-color]=\"divider.style.color\"\n ></div>\n } @else if (isChart(item)) {\n @let chart = item;\n @defer (on immediate) {\n <widget-chart-element\n [class.is-progress]=\"chart.type === 'progress'\"\n [element]=\"chart\"\n [sensitiveHidden]=\"isSensitive(chart)\"\n [locale]=\"locale()\"\n />\n } @placeholder {\n <div class=\"chart-placeholder\" aria-hidden=\"true\"></div>\n }\n }\n </div>\n</div>\n",
606
854
  styles: [":host{position:relative;min-width:0;min-height:0;display:flex;z-index:1}.widget-element{position:relative;min-width:0;min-height:0;display:flex;z-index:1;width:100%;height:100%}.element-content{display:flex;width:100%;height:100%;min-width:0;min-height:0}.element-content:has(widget-chart-element){align-items:stretch}.element-content>widget-chart-element{flex:0 1 auto;width:100%;height:100%;max-width:100%;max-height:100%}.element-content>widget-chart-element.is-progress{height:auto}.chart-placeholder{width:100%;height:100%}.text-element{display:inline-block;overflow:visible;line-height:1.2}img{width:100%;height:100%;display:block}.image-overlay{position:absolute;inset:0;z-index:1;pointer-events:none;border-radius:inherit}.image-placeholder{display:flex;width:100%;height:100%;align-items:center;justify-content:center;overflow:hidden;background:#dfe5ee}.image-placeholder svg{width:min(56px,45%);height:auto;fill:none;stroke:#8c9ab0;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}.divider-element{width:100%;height:2px;flex:0 0 auto;border-radius:var(--widget-renderer-pill-radius, 999px)}.divider-element.is-vertical{width:2px;height:100%}.element-content.is-sensitive{filter:blur(var(--sensitive-blur, 2px));-webkit-user-select:none;user-select:none}.action-button{pointer-events:var(--widget-renderer-actions-pointer-events, auto);min-height:30px;border-radius:var(--widget-renderer-radius, 8px);padding:6px 10px;font:600 12px/1.1 inherit;cursor:var(--widget-renderer-action-cursor, pointer)}.action-button.show-only-on-hover{opacity:0;pointer-events:none;transform:translateY(2px);transition:opacity var(--widget-renderer-transition-duration, .16s) var(--widget-renderer-transition-easing, ease),transform var(--widget-renderer-transition-duration, .16s) var(--widget-renderer-transition-easing, ease)}:host-context(.widget-shell:hover) .action-button.show-only-on-hover,:host-context(.widget-shell:focus-within) .action-button.show-only-on-hover{opacity:1;pointer-events:auto;transform:translateY(0)}@media(prefers-reduced-motion:reduce){.action-button.show-only-on-hover{transition:none}}.action-button.solid{border:1px solid var(--widget-renderer-action-background, #151515);background:var(--widget-renderer-action-background, #151515);color:var(--widget-renderer-action-color, #fff)}.action-button.outline{border:1px solid var(--widget-renderer-action-outline-border, #737373);background:var(--widget-renderer-action-outline-background, #fff);color:var(--widget-renderer-action-outline-color, #151515)}\n"],
607
- deferBlockDependencies: [() => [import("./lieselthuriot-widget-renderer-chart-element-renderer-BO3V-jYo.mjs").then((m) => m.ChartElementRenderer)]]
855
+ deferBlockDependencies: [() => [import("./lieselthuriot-widget-renderer-chart-element-renderer-wVHdIYuJ.mjs").then((m) => m.ChartElementRenderer)]]
608
856
  });
609
857
  };
610
858
  i0.ɵɵngDeclareClassMetadataAsync({
611
859
  minVersion: "18.0.0",
612
- version: "22.2.3",
860
+ version: "22.2.4",
613
861
  ngImport: i0,
614
862
  type: WidgetElementRenderer,
615
- resolveDeferredDeps: () => [import("./lieselthuriot-widget-renderer-chart-element-renderer-BO3V-jYo.mjs").then((m) => m.ChartElementRenderer)],
863
+ resolveDeferredDeps: () => [import("./lieselthuriot-widget-renderer-chart-element-renderer-wVHdIYuJ.mjs").then((m) => m.ChartElementRenderer)],
616
864
  resolveMetadata: (ChartElementRenderer) => ({
617
865
  decorators: [{
618
866
  type: Component,
@@ -666,7 +914,7 @@ var WidgetSkeleton = class WidgetSkeleton {
666
914
  locale = input(defaultWidgetRendererLocale, ...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
667
915
  static ɵfac = i0.ɵɵngDeclareFactory({
668
916
  minVersion: "12.0.0",
669
- version: "22.2.3",
917
+ version: "22.2.4",
670
918
  ngImport: i0,
671
919
  type: WidgetSkeleton,
672
920
  deps: [],
@@ -674,7 +922,7 @@ var WidgetSkeleton = class WidgetSkeleton {
674
922
  });
675
923
  static ɵcmp = i0.ɵɵngDeclareComponent({
676
924
  minVersion: "17.1.0",
677
- version: "22.2.3",
925
+ version: "22.2.4",
678
926
  type: WidgetSkeleton,
679
927
  isStandalone: true,
680
928
  selector: "widget-skeleton",
@@ -708,7 +956,7 @@ var WidgetSkeleton = class WidgetSkeleton {
708
956
  };
709
957
  i0.ɵɵngDeclareClassMetadata({
710
958
  minVersion: "12.0.0",
711
- version: "22.2.3",
959
+ version: "22.2.4",
712
960
  ngImport: i0,
713
961
  type: WidgetSkeleton,
714
962
  decorators: [{
@@ -752,7 +1000,7 @@ var WidgetBackground = class WidgetBackground {
752
1000
  image = input("none", ...ngDevMode ? [{ debugName: "image" }] : /* istanbul ignore next */ []);
753
1001
  static ɵfac = i0.ɵɵngDeclareFactory({
754
1002
  minVersion: "12.0.0",
755
- version: "22.2.3",
1003
+ version: "22.2.4",
756
1004
  ngImport: i0,
757
1005
  type: WidgetBackground,
758
1006
  deps: [],
@@ -760,7 +1008,7 @@ var WidgetBackground = class WidgetBackground {
760
1008
  });
761
1009
  static ɵcmp = i0.ɵɵngDeclareComponent({
762
1010
  minVersion: "17.1.0",
763
- version: "22.2.3",
1011
+ version: "22.2.4",
764
1012
  type: WidgetBackground,
765
1013
  isStandalone: true,
766
1014
  selector: "widget-background",
@@ -812,7 +1060,7 @@ var WidgetBackground = class WidgetBackground {
812
1060
  };
813
1061
  i0.ɵɵngDeclareClassMetadata({
814
1062
  minVersion: "12.0.0",
815
- version: "22.2.3",
1063
+ version: "22.2.4",
816
1064
  ngImport: i0,
817
1065
  type: WidgetBackground,
818
1066
  decorators: [{
@@ -876,7 +1124,7 @@ var WidgetToolbar = class WidgetToolbar {
876
1124
  sensitiveToggled = output();
877
1125
  static ɵfac = i0.ɵɵngDeclareFactory({
878
1126
  minVersion: "12.0.0",
879
- version: "22.2.3",
1127
+ version: "22.2.4",
880
1128
  ngImport: i0,
881
1129
  type: WidgetToolbar,
882
1130
  deps: [],
@@ -884,7 +1132,7 @@ var WidgetToolbar = class WidgetToolbar {
884
1132
  });
885
1133
  static ɵcmp = i0.ɵɵngDeclareComponent({
886
1134
  minVersion: "17.0.0",
887
- version: "22.2.3",
1135
+ version: "22.2.4",
888
1136
  type: WidgetToolbar,
889
1137
  isStandalone: true,
890
1138
  selector: "widget-toolbar",
@@ -968,7 +1216,7 @@ var WidgetToolbar = class WidgetToolbar {
968
1216
  };
969
1217
  i0.ɵɵngDeclareClassMetadata({
970
1218
  minVersion: "12.0.0",
971
- version: "22.2.3",
1219
+ version: "22.2.4",
972
1220
  ngImport: i0,
973
1221
  type: WidgetToolbar,
974
1222
  decorators: [{
@@ -1064,7 +1312,7 @@ var WidgetValidationOverlay = class WidgetValidationOverlay {
1064
1312
  issues = input.required(...ngDevMode ? [{ debugName: "issues" }] : /* istanbul ignore next */ []);
1065
1313
  static ɵfac = i0.ɵɵngDeclareFactory({
1066
1314
  minVersion: "12.0.0",
1067
- version: "22.2.3",
1315
+ version: "22.2.4",
1068
1316
  ngImport: i0,
1069
1317
  type: WidgetValidationOverlay,
1070
1318
  deps: [],
@@ -1072,7 +1320,7 @@ var WidgetValidationOverlay = class WidgetValidationOverlay {
1072
1320
  });
1073
1321
  static ɵcmp = i0.ɵɵngDeclareComponent({
1074
1322
  minVersion: "17.0.0",
1075
- version: "22.2.3",
1323
+ version: "22.2.4",
1076
1324
  type: WidgetValidationOverlay,
1077
1325
  isStandalone: true,
1078
1326
  selector: "widget-validation-overlay",
@@ -1101,7 +1349,7 @@ var WidgetValidationOverlay = class WidgetValidationOverlay {
1101
1349
  };
1102
1350
  i0.ɵɵngDeclareClassMetadata({
1103
1351
  minVersion: "12.0.0",
1104
- version: "22.2.3",
1352
+ version: "22.2.4",
1105
1353
  ngImport: i0,
1106
1354
  type: WidgetValidationOverlay,
1107
1355
  decorators: [{
@@ -1136,6 +1384,7 @@ var WidgetRenderer = class WidgetRenderer {
1136
1384
  loading = input(false, ...ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []);
1137
1385
  darkMode = input(false, ...ngDevMode ? [{ debugName: "darkMode" }] : /* istanbul ignore next */ []);
1138
1386
  locale = input({}, ...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
1387
+ validationRules = input([], ...ngDevMode ? [{ debugName: "validationRules" }] : /* istanbul ignore next */ []);
1139
1388
  refreshRequested = output();
1140
1389
  actionRequested = output();
1141
1390
  sensitiveVisible = signal(false, ...ngDevMode ? [{ debugName: "sensitiveVisible" }] : /* istanbul ignore next */ []);
@@ -1146,7 +1395,7 @@ var WidgetRenderer = class WidgetRenderer {
1146
1395
  return widget === null ? {
1147
1396
  success: false,
1148
1397
  issues: []
1149
- } : safeParseWidgetInput(widget);
1398
+ } : safeParseWidgetInput(widget, { rules: this.validationRules() });
1150
1399
  }, ...ngDevMode ? [{ debugName: "parsedWidget" }] : /* istanbul ignore next */ []);
1151
1400
  isLoading = computed(() => this.loading() !== false, ...ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []);
1152
1401
  resolvedLocale = computed(() => ({
@@ -1164,10 +1413,8 @@ var WidgetRenderer = class WidgetRenderer {
1164
1413
  const loading = this.loading();
1165
1414
  return typeof loading === "string" ? loading : "square";
1166
1415
  }, ...ngDevMode ? [{ debugName: "loadingSize" }] : /* istanbul ignore next */ []);
1167
- validationIssues = computed(() => {
1168
- const result = this.parsedWidget();
1169
- return result.success ? [] : result.issues;
1170
- }, ...ngDevMode ? [{ debugName: "validationIssues" }] : /* istanbul ignore next */ []);
1416
+ validationIssues = computed(() => this.parsedWidget().issues, ...ngDevMode ? [{ debugName: "validationIssues" }] : /* istanbul ignore next */ []);
1417
+ blockingIssues = computed(() => this.validationIssues().filter((issue) => issue.severity !== "report"), ...ngDevMode ? [{ debugName: "blockingIssues" }] : /* istanbul ignore next */ []);
1171
1418
  renderWidget = computed(() => {
1172
1419
  const result = this.parsedWidget();
1173
1420
  if (!result.success) return {
@@ -1215,7 +1462,7 @@ var WidgetRenderer = class WidgetRenderer {
1215
1462
  }
1216
1463
  static ɵfac = i0.ɵɵngDeclareFactory({
1217
1464
  minVersion: "12.0.0",
1218
- version: "22.2.3",
1465
+ version: "22.2.4",
1219
1466
  ngImport: i0,
1220
1467
  type: WidgetRenderer,
1221
1468
  deps: [],
@@ -1223,7 +1470,7 @@ var WidgetRenderer = class WidgetRenderer {
1223
1470
  });
1224
1471
  static ɵcmp = i0.ɵɵngDeclareComponent({
1225
1472
  minVersion: "17.0.0",
1226
- version: "22.2.3",
1473
+ version: "22.2.4",
1227
1474
  type: WidgetRenderer,
1228
1475
  isStandalone: true,
1229
1476
  selector: "widget-renderer",
@@ -1255,6 +1502,13 @@ var WidgetRenderer = class WidgetRenderer {
1255
1502
  isSignal: true,
1256
1503
  isRequired: false,
1257
1504
  transformFunction: null
1505
+ },
1506
+ validationRules: {
1507
+ classPropertyName: "validationRules",
1508
+ publicName: "validationRules",
1509
+ isSignal: true,
1510
+ isRequired: false,
1511
+ transformFunction: null
1258
1512
  }
1259
1513
  },
1260
1514
  outputs: {
@@ -1270,8 +1524,8 @@ var WidgetRenderer = class WidgetRenderer {
1270
1524
  isSignal: true
1271
1525
  }],
1272
1526
  ngImport: i0,
1273
- template: "@if (isLoading()) {\n <widget-skeleton [size]=\"loadingSize()\" [darkMode]=\"darkMode()\" [locale]=\"resolvedLocale()\" />\n} @else {\n @let widget = renderWidget();\n <section class=\"widget-shell\" [style.width.px]=\"widgetWidth()\" [style.height.px]=\"widgetHeight()\">\n <div\n class=\"widget-viewport\"\n [style.background-color]=\"widget.background.color || 'transparent'\"\n >\n <widget-background\n [background]=\"widget.background\"\n [sensitive]=\"backgroundSensitive()\"\n [image]=\"backgroundImage()\"\n />\n @if (validationIssues().length > 0) {\n <widget-validation-overlay [issues]=\"validationIssues()\" />\n }\n <widget-toolbar\n [refresh]=\"widget.refresh\"\n [hasSensitiveElements]=\"hasSensitiveElements()\"\n [sensitiveVisible]=\"sensitiveVisible()\"\n [locale]=\"resolvedLocale()\"\n (refreshRequested)=\"refreshRequested.emit()\"\n (sensitiveToggled)=\"toggleSensitive()\"\n />\n <div\n class=\"widget-grid\"\n [style.grid-template-rows]=\"gridRows()\"\n [style.grid-template-columns]=\"gridColumns()\"\n >\n @for (element of widget.elements; track $index) {\n <widget-element-renderer\n [element]=\"element\"\n [sensitiveVisible]=\"sensitiveVisible()\"\n [locale]=\"resolvedLocale()\"\n (actionRequested)=\"actionRequested.emit($event)\"\n />\n }\n </div>\n </div>\n <div #tooltipLayer class=\"widget-tooltip-layer\">\n @if (tooltip.progress(); as progress) {\n <span\n class=\"progress-tooltip\"\n role=\"tooltip\"\n [style.--tooltip-x.px]=\"progress.x\"\n [style.--tooltip-y.px]=\"progress.y\"\n [style.--tooltip-border]=\"progress.color || 'rgb(0 0 0 / 0.12)'\"\n ><span class=\"tooltip-marker\" [style.background]=\"progress.color\"></span\n ><span>{{ progress.label }}</span\n ><strong>{{ progress.value }}</strong></span\n >\n }\n </div>\n </section>\n}\n",
1274
- styles: [":host{display:block;--widget-renderer-radius: 8px;--widget-renderer-pill-radius: 999px;--widget-renderer-control-size: 28px;--widget-renderer-content-padding: 18px;--widget-renderer-shadow: 0 18px 40px #10182c38;--widget-renderer-transition-duration: .16s;--widget-renderer-transition-easing: ease;--widget-renderer-control-background: #10182ccc;--widget-renderer-control-border: #ffffff38;--widget-renderer-control-hover-border: #17233c;--widget-renderer-control-hover-background: #fff;--widget-renderer-control-hover-color: #17233c;--widget-renderer-action-background: #151515;--widget-renderer-action-color: #fff;--widget-renderer-action-outline-border: #737373;--widget-renderer-action-outline-background: #fff;--widget-renderer-action-outline-color: #151515}.widget-skeleton{position:relative;width:274px;max-width:100%;height:254px;box-sizing:border-box;overflow:hidden;border-radius:var(--widget-renderer-radius);background:#dfe5ee;box-shadow:var(--widget-renderer-shadow)}.widget-skeleton.is-wide{width:556px}.widget-skeleton.is-mini{width:133px;height:123px}.widget-skeleton.is-compact{width:274px;height:123px}.widget-skeleton.is-tall{width:133px;height:254px}.widget-skeleton.is-mega{width:556px;height:516px}.widget-skeleton.is-banner{width:274px;height:516px}.widget-skeleton.is-hero{width:1120px;height:516px}.widget-skeleton.is-mini .skeleton-toolbar{top:8px;right:8px;width:22px;height:22px}.widget-skeleton.is-mini .skeleton-content{box-sizing:border-box;height:100%;gap:3px;padding:24px 12px 8px}.widget-skeleton.is-mini .skeleton-line{height:6px}.widget-skeleton.is-mini .skeleton-line-title{height:10px}.widget-skeleton.is-mini .skeleton-chart{flex:1 1 auto;min-height:20px;margin-top:0}.widget-skeleton.is-mini .skeleton-button{width:42px;height:18px}.widget-skeleton:is(.is-compact,.is-tall) .skeleton-content{box-sizing:border-box;height:100%;gap:5px;padding:30px 12px 10px}.widget-skeleton.is-compact .skeleton-chart{min-height:28px;margin-top:0}.widget-skeleton.is-tall .skeleton-chart{flex:1 1 auto;min-height:45px;margin-top:0}.widget-skeleton:is(.is-mega,.is-banner,.is-hero) .skeleton-content{box-sizing:border-box;height:100%}.widget-skeleton:is(.is-mega,.is-banner,.is-hero) .skeleton-chart{flex:1 1 auto;min-height:180px}.widget-skeleton.is-dark{background:#182236}.skeleton-line,.skeleton-chart,.skeleton-button{background:linear-gradient(100deg,#dfe5ee 20%,#f2f5f9,#dfe5ee 70%);background-size:200% 100%;animation:skeleton-shimmer 1.8s linear infinite}.widget-skeleton.is-dark .skeleton-line,.widget-skeleton.is-dark .skeleton-chart,.widget-skeleton.is-dark .skeleton-button{background:linear-gradient(100deg,#182236 20%,#2b3b59,#182236 70%);background-size:200% 100%}.skeleton-toolbar{position:absolute;top:12px;right:12px;width:var(--widget-renderer-control-size);height:var(--widget-renderer-control-size);border-radius:var(--widget-renderer-radius);background:#c4cedd}.widget-skeleton.is-dark .skeleton-toolbar{background:#2b3b59}.skeleton-content{display:flex;flex-direction:column;gap:10px;padding:44px var(--widget-renderer-content-padding) 16px}.skeleton-line{display:block;height:10px;border-radius:var(--widget-renderer-pill-radius)}.skeleton-line-small{width:28%}.skeleton-line-title{width:72%;height:20px}.skeleton-line-copy{width:58%}.skeleton-chart{width:100%;height:64px;margin-top:2px;border-radius:var(--widget-renderer-radius)}.skeleton-button{align-self:flex-end;width:58px;height:30px;border-radius:var(--widget-renderer-radius)}@keyframes skeleton-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){.skeleton-line,.skeleton-chart,.skeleton-button{animation:none}}.widget-shell{position:relative;overflow:visible;box-sizing:border-box;color:#f6f7fb}.widget-viewport{position:absolute;inset:0;overflow:hidden;border-radius:var(--widget-renderer-radius);box-shadow:var(--widget-renderer-shadow)}.widget-viewport:after{position:absolute;inset:0;z-index:5;box-sizing:border-box;border:1px solid #10182c26;border-radius:inherit;content:\"\";pointer-events:none}.widget-tooltip-layer{position:absolute;inset:0;z-index:6;overflow:visible;pointer-events:none}.progress-tooltip{--tooltip-border: rgb(0 0 0 / .12);position:absolute;z-index:4;top:var(--tooltip-y);left:var(--tooltip-x);display:flex;min-width:104px;align-items:center;gap:7px;padding:10px;transform:translate(-50%,calc(-100% - 10px));border:1px solid var(--tooltip-border);border-radius:var(--widget-renderer-radius, 4px);background:var(--widget-renderer-control-hover-background, #fff);box-shadow:1px 2px 10px #0003;color:#6e7079;font-size:14px;font-weight:400;line-height:1;white-space:nowrap}.progress-tooltip strong{margin-inline-start:auto;color:#464646;font-size:14px;font-weight:900}.tooltip-marker{width:9px;height:9px;flex:0 0 auto;border-radius:50%}.widget-grid{position:absolute;inset:0;display:grid;gap:8px;padding:18px}.chart-placeholder{width:100%;height:100%}.element-content{display:flex;width:100%;height:100%;min-width:0;min-height:0}.element-content:has(widget-chart-element){align-items:stretch}.element-content>widget-chart-element{flex:0 1 auto;width:100%;height:100%;max-width:100%;max-height:100%}.element-content>widget-chart-element.is-progress{height:auto}.divider-element{width:100%;height:2px;flex:0 0 auto;border-radius:var(--widget-renderer-pill-radius)}.divider-element.is-vertical{width:2px;height:100%}.element-content.is-sensitive{filter:blur(var(--sensitive-blur, 2px));-webkit-user-select:none;user-select:none}.text-element{display:inline-block;overflow:visible;line-height:1.2}img{width:100%;height:100%;display:block}.image-overlay{position:absolute;inset:0;z-index:1;pointer-events:none;border-radius:inherit}.image-placeholder{display:flex;width:100%;height:100%;align-items:center;justify-content:center;overflow:hidden;background:#dfe5ee}.image-placeholder svg{width:min(56px,45%);height:auto;fill:none;stroke:#8c9ab0;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}.action-button{min-height:30px;border-radius:var(--widget-renderer-radius);padding:6px 10px;font:600 12px/1.1 inherit;cursor:var(--widget-renderer-action-cursor, pointer)}.action-button.show-only-on-hover{opacity:0;pointer-events:none;transform:translateY(2px);transition:opacity var(--widget-renderer-transition-duration) var(--widget-renderer-transition-easing),transform var(--widget-renderer-transition-duration) var(--widget-renderer-transition-easing)}.widget-shell:hover .action-button.show-only-on-hover,.widget-shell:focus-within .action-button.show-only-on-hover{opacity:1;pointer-events:auto;transform:translateY(0)}.action-button.solid{border:1px solid #151515;background:#151515;color:#fff}.action-button.outline{border:1px solid #737373;background:#fff;color:#151515}\n"],
1527
+ template: "@if (isLoading()) {\n <widget-skeleton [size]=\"loadingSize()\" [darkMode]=\"darkMode()\" [locale]=\"resolvedLocale()\" />\n} @else {\n @let widget = renderWidget();\n <section class=\"widget-shell\" [style.width.px]=\"widgetWidth()\" [style.height.px]=\"widgetHeight()\">\n <div\n class=\"widget-viewport\"\n [style.background-color]=\"widget.background.color || 'transparent'\"\n >\n <widget-background\n [background]=\"widget.background\"\n [sensitive]=\"backgroundSensitive()\"\n [image]=\"backgroundImage()\"\n />\n @if (blockingIssues().length > 0) {\n <widget-validation-overlay [issues]=\"blockingIssues()\" />\n }\n <widget-toolbar\n [refresh]=\"widget.refresh\"\n [hasSensitiveElements]=\"hasSensitiveElements()\"\n [sensitiveVisible]=\"sensitiveVisible()\"\n [locale]=\"resolvedLocale()\"\n (refreshRequested)=\"refreshRequested.emit()\"\n (sensitiveToggled)=\"toggleSensitive()\"\n />\n <div\n class=\"widget-grid\"\n [style.grid-template-rows]=\"gridRows()\"\n [style.grid-template-columns]=\"gridColumns()\"\n >\n @for (element of widget.elements; track $index) {\n <widget-element-renderer\n [element]=\"element\"\n [sensitiveVisible]=\"sensitiveVisible()\"\n [locale]=\"resolvedLocale()\"\n (actionRequested)=\"actionRequested.emit($event)\"\n />\n }\n </div>\n </div>\n <div #tooltipLayer class=\"widget-tooltip-layer\">\n @if (tooltip.state(); as tip) {\n <span\n class=\"chart-tooltip\"\n role=\"tooltip\"\n [style.--tooltip-x.px]=\"tip.x\"\n [style.--tooltip-y.px]=\"tip.y\"\n [style.--tooltip-border]=\"tip.color || 'rgb(0 0 0 / 0.12)'\"\n ><span class=\"tooltip-marker\" [style.background]=\"tip.color\"></span\n ><span>{{ tip.label }}</span\n ><strong>{{ tip.value }}</strong></span\n >\n }\n </div>\n </section>\n}\n",
1528
+ styles: [":host{display:block;--widget-renderer-radius: 8px;--widget-renderer-pill-radius: 999px;--widget-renderer-control-size: 28px;--widget-renderer-content-padding: 18px;--widget-renderer-shadow: 0 18px 40px #10182c38;--widget-renderer-transition-duration: .16s;--widget-renderer-transition-easing: ease;--widget-renderer-control-background: #10182ccc;--widget-renderer-control-border: #ffffff38;--widget-renderer-control-hover-border: #17233c;--widget-renderer-control-hover-background: #fff;--widget-renderer-control-hover-color: #17233c;--widget-renderer-action-background: #151515;--widget-renderer-action-color: #fff;--widget-renderer-action-outline-border: #737373;--widget-renderer-action-outline-background: #fff;--widget-renderer-action-outline-color: #151515}.widget-skeleton{position:relative;width:274px;max-width:100%;height:254px;box-sizing:border-box;overflow:hidden;border-radius:var(--widget-renderer-radius);background:#dfe5ee;box-shadow:var(--widget-renderer-shadow)}.widget-skeleton.is-wide{width:556px}.widget-skeleton.is-mini{width:133px;height:123px}.widget-skeleton.is-compact{width:274px;height:123px}.widget-skeleton.is-tall{width:133px;height:254px}.widget-skeleton.is-mega{width:556px;height:516px}.widget-skeleton.is-banner{width:274px;height:516px}.widget-skeleton.is-hero{width:1120px;height:516px}.widget-skeleton.is-mini .skeleton-toolbar{top:8px;right:8px;width:22px;height:22px}.widget-skeleton.is-mini .skeleton-content{box-sizing:border-box;height:100%;gap:3px;padding:24px 12px 8px}.widget-skeleton.is-mini .skeleton-line{height:6px}.widget-skeleton.is-mini .skeleton-line-title{height:10px}.widget-skeleton.is-mini .skeleton-chart{flex:1 1 auto;min-height:20px;margin-top:0}.widget-skeleton.is-mini .skeleton-button{width:42px;height:18px}.widget-skeleton:is(.is-compact,.is-tall) .skeleton-content{box-sizing:border-box;height:100%;gap:5px;padding:30px 12px 10px}.widget-skeleton.is-compact .skeleton-chart{min-height:28px;margin-top:0}.widget-skeleton.is-tall .skeleton-chart{flex:1 1 auto;min-height:45px;margin-top:0}.widget-skeleton:is(.is-mega,.is-banner,.is-hero) .skeleton-content{box-sizing:border-box;height:100%}.widget-skeleton:is(.is-mega,.is-banner,.is-hero) .skeleton-chart{flex:1 1 auto;min-height:180px}.widget-skeleton.is-dark{background:#182236}.skeleton-line,.skeleton-chart,.skeleton-button{background:linear-gradient(100deg,#dfe5ee 20%,#f2f5f9,#dfe5ee 70%);background-size:200% 100%;animation:skeleton-shimmer 1.8s linear infinite}.widget-skeleton.is-dark .skeleton-line,.widget-skeleton.is-dark .skeleton-chart,.widget-skeleton.is-dark .skeleton-button{background:linear-gradient(100deg,#182236 20%,#2b3b59,#182236 70%);background-size:200% 100%}.skeleton-toolbar{position:absolute;top:12px;right:12px;width:var(--widget-renderer-control-size);height:var(--widget-renderer-control-size);border-radius:var(--widget-renderer-radius);background:#c4cedd}.widget-skeleton.is-dark .skeleton-toolbar{background:#2b3b59}.skeleton-content{display:flex;flex-direction:column;gap:10px;padding:44px var(--widget-renderer-content-padding) 16px}.skeleton-line{display:block;height:10px;border-radius:var(--widget-renderer-pill-radius)}.skeleton-line-small{width:28%}.skeleton-line-title{width:72%;height:20px}.skeleton-line-copy{width:58%}.skeleton-chart{width:100%;height:64px;margin-top:2px;border-radius:var(--widget-renderer-radius)}.skeleton-button{align-self:flex-end;width:58px;height:30px;border-radius:var(--widget-renderer-radius)}@keyframes skeleton-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){.skeleton-line,.skeleton-chart,.skeleton-button{animation:none}}.widget-shell{position:relative;overflow:visible;box-sizing:border-box;color:#f6f7fb}.widget-viewport{position:absolute;inset:0;overflow:hidden;border-radius:var(--widget-renderer-radius);box-shadow:var(--widget-renderer-shadow)}.widget-viewport:after{position:absolute;inset:0;z-index:5;box-sizing:border-box;border:1px solid #10182c26;border-radius:inherit;content:\"\";pointer-events:none}.widget-tooltip-layer{position:absolute;inset:0;z-index:6;overflow:visible;pointer-events:none}.chart-tooltip{--tooltip-border: rgb(0 0 0 / .12);position:absolute;z-index:4;top:var(--tooltip-y);left:var(--tooltip-x);display:flex;min-width:104px;align-items:center;gap:7px;padding:10px;transform:translate(-50%,calc(-100% - 10px));border:1px solid var(--tooltip-border);border-radius:var(--widget-renderer-radius, 4px);background:var(--widget-renderer-control-hover-background, #fff);box-shadow:1px 2px 10px #0003;color:#6e7079;font-size:14px;font-weight:400;line-height:1;white-space:nowrap}.chart-tooltip strong{margin-inline-start:auto;color:#464646;font-size:14px;font-weight:900}.tooltip-marker{width:9px;height:9px;flex:0 0 auto;border-radius:50%}.widget-grid{position:absolute;inset:0;display:grid;gap:8px;padding:18px}.chart-placeholder{width:100%;height:100%}.element-content{display:flex;width:100%;height:100%;min-width:0;min-height:0}.element-content:has(widget-chart-element){align-items:stretch}.element-content>widget-chart-element{flex:0 1 auto;width:100%;height:100%;max-width:100%;max-height:100%}.element-content>widget-chart-element.is-progress{height:auto}.divider-element{width:100%;height:2px;flex:0 0 auto;border-radius:var(--widget-renderer-pill-radius)}.divider-element.is-vertical{width:2px;height:100%}.element-content.is-sensitive{filter:blur(var(--sensitive-blur, 2px));-webkit-user-select:none;user-select:none}.text-element{display:inline-block;overflow:visible;line-height:1.2}img{width:100%;height:100%;display:block}.image-overlay{position:absolute;inset:0;z-index:1;pointer-events:none;border-radius:inherit}.image-placeholder{display:flex;width:100%;height:100%;align-items:center;justify-content:center;overflow:hidden;background:#dfe5ee}.image-placeholder svg{width:min(56px,45%);height:auto;fill:none;stroke:#8c9ab0;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}.action-button{min-height:30px;border-radius:var(--widget-renderer-radius);padding:6px 10px;font:600 12px/1.1 inherit;cursor:var(--widget-renderer-action-cursor, pointer)}.action-button.show-only-on-hover{opacity:0;pointer-events:none;transform:translateY(2px);transition:opacity var(--widget-renderer-transition-duration) var(--widget-renderer-transition-easing),transform var(--widget-renderer-transition-duration) var(--widget-renderer-transition-easing)}.widget-shell:hover .action-button.show-only-on-hover,.widget-shell:focus-within .action-button.show-only-on-hover{opacity:1;pointer-events:auto;transform:translateY(0)}.action-button.solid{border:1px solid #151515;background:#151515;color:#fff}.action-button.outline{border:1px solid #737373;background:#fff;color:#151515}\n"],
1275
1529
  dependencies: [
1276
1530
  {
1277
1531
  kind: "component",
@@ -1327,7 +1581,7 @@ var WidgetRenderer = class WidgetRenderer {
1327
1581
  };
1328
1582
  i0.ɵɵngDeclareClassMetadata({
1329
1583
  minVersion: "12.0.0",
1330
- version: "22.2.3",
1584
+ version: "22.2.4",
1331
1585
  ngImport: i0,
1332
1586
  type: WidgetRenderer,
1333
1587
  decorators: [{
@@ -1342,8 +1596,8 @@ i0.ɵɵngDeclareClassMetadata({
1342
1596
  WidgetToolbar,
1343
1597
  WidgetValidationOverlay
1344
1598
  ],
1345
- template: "@if (isLoading()) {\n <widget-skeleton [size]=\"loadingSize()\" [darkMode]=\"darkMode()\" [locale]=\"resolvedLocale()\" />\n} @else {\n @let widget = renderWidget();\n <section class=\"widget-shell\" [style.width.px]=\"widgetWidth()\" [style.height.px]=\"widgetHeight()\">\n <div\n class=\"widget-viewport\"\n [style.background-color]=\"widget.background.color || 'transparent'\"\n >\n <widget-background\n [background]=\"widget.background\"\n [sensitive]=\"backgroundSensitive()\"\n [image]=\"backgroundImage()\"\n />\n @if (validationIssues().length > 0) {\n <widget-validation-overlay [issues]=\"validationIssues()\" />\n }\n <widget-toolbar\n [refresh]=\"widget.refresh\"\n [hasSensitiveElements]=\"hasSensitiveElements()\"\n [sensitiveVisible]=\"sensitiveVisible()\"\n [locale]=\"resolvedLocale()\"\n (refreshRequested)=\"refreshRequested.emit()\"\n (sensitiveToggled)=\"toggleSensitive()\"\n />\n <div\n class=\"widget-grid\"\n [style.grid-template-rows]=\"gridRows()\"\n [style.grid-template-columns]=\"gridColumns()\"\n >\n @for (element of widget.elements; track $index) {\n <widget-element-renderer\n [element]=\"element\"\n [sensitiveVisible]=\"sensitiveVisible()\"\n [locale]=\"resolvedLocale()\"\n (actionRequested)=\"actionRequested.emit($event)\"\n />\n }\n </div>\n </div>\n <div #tooltipLayer class=\"widget-tooltip-layer\">\n @if (tooltip.progress(); as progress) {\n <span\n class=\"progress-tooltip\"\n role=\"tooltip\"\n [style.--tooltip-x.px]=\"progress.x\"\n [style.--tooltip-y.px]=\"progress.y\"\n [style.--tooltip-border]=\"progress.color || 'rgb(0 0 0 / 0.12)'\"\n ><span class=\"tooltip-marker\" [style.background]=\"progress.color\"></span\n ><span>{{ progress.label }}</span\n ><strong>{{ progress.value }}</strong></span\n >\n }\n </div>\n </section>\n}\n",
1346
- styles: [":host{display:block;--widget-renderer-radius: 8px;--widget-renderer-pill-radius: 999px;--widget-renderer-control-size: 28px;--widget-renderer-content-padding: 18px;--widget-renderer-shadow: 0 18px 40px #10182c38;--widget-renderer-transition-duration: .16s;--widget-renderer-transition-easing: ease;--widget-renderer-control-background: #10182ccc;--widget-renderer-control-border: #ffffff38;--widget-renderer-control-hover-border: #17233c;--widget-renderer-control-hover-background: #fff;--widget-renderer-control-hover-color: #17233c;--widget-renderer-action-background: #151515;--widget-renderer-action-color: #fff;--widget-renderer-action-outline-border: #737373;--widget-renderer-action-outline-background: #fff;--widget-renderer-action-outline-color: #151515}.widget-skeleton{position:relative;width:274px;max-width:100%;height:254px;box-sizing:border-box;overflow:hidden;border-radius:var(--widget-renderer-radius);background:#dfe5ee;box-shadow:var(--widget-renderer-shadow)}.widget-skeleton.is-wide{width:556px}.widget-skeleton.is-mini{width:133px;height:123px}.widget-skeleton.is-compact{width:274px;height:123px}.widget-skeleton.is-tall{width:133px;height:254px}.widget-skeleton.is-mega{width:556px;height:516px}.widget-skeleton.is-banner{width:274px;height:516px}.widget-skeleton.is-hero{width:1120px;height:516px}.widget-skeleton.is-mini .skeleton-toolbar{top:8px;right:8px;width:22px;height:22px}.widget-skeleton.is-mini .skeleton-content{box-sizing:border-box;height:100%;gap:3px;padding:24px 12px 8px}.widget-skeleton.is-mini .skeleton-line{height:6px}.widget-skeleton.is-mini .skeleton-line-title{height:10px}.widget-skeleton.is-mini .skeleton-chart{flex:1 1 auto;min-height:20px;margin-top:0}.widget-skeleton.is-mini .skeleton-button{width:42px;height:18px}.widget-skeleton:is(.is-compact,.is-tall) .skeleton-content{box-sizing:border-box;height:100%;gap:5px;padding:30px 12px 10px}.widget-skeleton.is-compact .skeleton-chart{min-height:28px;margin-top:0}.widget-skeleton.is-tall .skeleton-chart{flex:1 1 auto;min-height:45px;margin-top:0}.widget-skeleton:is(.is-mega,.is-banner,.is-hero) .skeleton-content{box-sizing:border-box;height:100%}.widget-skeleton:is(.is-mega,.is-banner,.is-hero) .skeleton-chart{flex:1 1 auto;min-height:180px}.widget-skeleton.is-dark{background:#182236}.skeleton-line,.skeleton-chart,.skeleton-button{background:linear-gradient(100deg,#dfe5ee 20%,#f2f5f9,#dfe5ee 70%);background-size:200% 100%;animation:skeleton-shimmer 1.8s linear infinite}.widget-skeleton.is-dark .skeleton-line,.widget-skeleton.is-dark .skeleton-chart,.widget-skeleton.is-dark .skeleton-button{background:linear-gradient(100deg,#182236 20%,#2b3b59,#182236 70%);background-size:200% 100%}.skeleton-toolbar{position:absolute;top:12px;right:12px;width:var(--widget-renderer-control-size);height:var(--widget-renderer-control-size);border-radius:var(--widget-renderer-radius);background:#c4cedd}.widget-skeleton.is-dark .skeleton-toolbar{background:#2b3b59}.skeleton-content{display:flex;flex-direction:column;gap:10px;padding:44px var(--widget-renderer-content-padding) 16px}.skeleton-line{display:block;height:10px;border-radius:var(--widget-renderer-pill-radius)}.skeleton-line-small{width:28%}.skeleton-line-title{width:72%;height:20px}.skeleton-line-copy{width:58%}.skeleton-chart{width:100%;height:64px;margin-top:2px;border-radius:var(--widget-renderer-radius)}.skeleton-button{align-self:flex-end;width:58px;height:30px;border-radius:var(--widget-renderer-radius)}@keyframes skeleton-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){.skeleton-line,.skeleton-chart,.skeleton-button{animation:none}}.widget-shell{position:relative;overflow:visible;box-sizing:border-box;color:#f6f7fb}.widget-viewport{position:absolute;inset:0;overflow:hidden;border-radius:var(--widget-renderer-radius);box-shadow:var(--widget-renderer-shadow)}.widget-viewport:after{position:absolute;inset:0;z-index:5;box-sizing:border-box;border:1px solid #10182c26;border-radius:inherit;content:\"\";pointer-events:none}.widget-tooltip-layer{position:absolute;inset:0;z-index:6;overflow:visible;pointer-events:none}.progress-tooltip{--tooltip-border: rgb(0 0 0 / .12);position:absolute;z-index:4;top:var(--tooltip-y);left:var(--tooltip-x);display:flex;min-width:104px;align-items:center;gap:7px;padding:10px;transform:translate(-50%,calc(-100% - 10px));border:1px solid var(--tooltip-border);border-radius:var(--widget-renderer-radius, 4px);background:var(--widget-renderer-control-hover-background, #fff);box-shadow:1px 2px 10px #0003;color:#6e7079;font-size:14px;font-weight:400;line-height:1;white-space:nowrap}.progress-tooltip strong{margin-inline-start:auto;color:#464646;font-size:14px;font-weight:900}.tooltip-marker{width:9px;height:9px;flex:0 0 auto;border-radius:50%}.widget-grid{position:absolute;inset:0;display:grid;gap:8px;padding:18px}.chart-placeholder{width:100%;height:100%}.element-content{display:flex;width:100%;height:100%;min-width:0;min-height:0}.element-content:has(widget-chart-element){align-items:stretch}.element-content>widget-chart-element{flex:0 1 auto;width:100%;height:100%;max-width:100%;max-height:100%}.element-content>widget-chart-element.is-progress{height:auto}.divider-element{width:100%;height:2px;flex:0 0 auto;border-radius:var(--widget-renderer-pill-radius)}.divider-element.is-vertical{width:2px;height:100%}.element-content.is-sensitive{filter:blur(var(--sensitive-blur, 2px));-webkit-user-select:none;user-select:none}.text-element{display:inline-block;overflow:visible;line-height:1.2}img{width:100%;height:100%;display:block}.image-overlay{position:absolute;inset:0;z-index:1;pointer-events:none;border-radius:inherit}.image-placeholder{display:flex;width:100%;height:100%;align-items:center;justify-content:center;overflow:hidden;background:#dfe5ee}.image-placeholder svg{width:min(56px,45%);height:auto;fill:none;stroke:#8c9ab0;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}.action-button{min-height:30px;border-radius:var(--widget-renderer-radius);padding:6px 10px;font:600 12px/1.1 inherit;cursor:var(--widget-renderer-action-cursor, pointer)}.action-button.show-only-on-hover{opacity:0;pointer-events:none;transform:translateY(2px);transition:opacity var(--widget-renderer-transition-duration) var(--widget-renderer-transition-easing),transform var(--widget-renderer-transition-duration) var(--widget-renderer-transition-easing)}.widget-shell:hover .action-button.show-only-on-hover,.widget-shell:focus-within .action-button.show-only-on-hover{opacity:1;pointer-events:auto;transform:translateY(0)}.action-button.solid{border:1px solid #151515;background:#151515;color:#fff}.action-button.outline{border:1px solid #737373;background:#fff;color:#151515}\n"]
1599
+ template: "@if (isLoading()) {\n <widget-skeleton [size]=\"loadingSize()\" [darkMode]=\"darkMode()\" [locale]=\"resolvedLocale()\" />\n} @else {\n @let widget = renderWidget();\n <section class=\"widget-shell\" [style.width.px]=\"widgetWidth()\" [style.height.px]=\"widgetHeight()\">\n <div\n class=\"widget-viewport\"\n [style.background-color]=\"widget.background.color || 'transparent'\"\n >\n <widget-background\n [background]=\"widget.background\"\n [sensitive]=\"backgroundSensitive()\"\n [image]=\"backgroundImage()\"\n />\n @if (blockingIssues().length > 0) {\n <widget-validation-overlay [issues]=\"blockingIssues()\" />\n }\n <widget-toolbar\n [refresh]=\"widget.refresh\"\n [hasSensitiveElements]=\"hasSensitiveElements()\"\n [sensitiveVisible]=\"sensitiveVisible()\"\n [locale]=\"resolvedLocale()\"\n (refreshRequested)=\"refreshRequested.emit()\"\n (sensitiveToggled)=\"toggleSensitive()\"\n />\n <div\n class=\"widget-grid\"\n [style.grid-template-rows]=\"gridRows()\"\n [style.grid-template-columns]=\"gridColumns()\"\n >\n @for (element of widget.elements; track $index) {\n <widget-element-renderer\n [element]=\"element\"\n [sensitiveVisible]=\"sensitiveVisible()\"\n [locale]=\"resolvedLocale()\"\n (actionRequested)=\"actionRequested.emit($event)\"\n />\n }\n </div>\n </div>\n <div #tooltipLayer class=\"widget-tooltip-layer\">\n @if (tooltip.state(); as tip) {\n <span\n class=\"chart-tooltip\"\n role=\"tooltip\"\n [style.--tooltip-x.px]=\"tip.x\"\n [style.--tooltip-y.px]=\"tip.y\"\n [style.--tooltip-border]=\"tip.color || 'rgb(0 0 0 / 0.12)'\"\n ><span class=\"tooltip-marker\" [style.background]=\"tip.color\"></span\n ><span>{{ tip.label }}</span\n ><strong>{{ tip.value }}</strong></span\n >\n }\n </div>\n </section>\n}\n",
1600
+ styles: [":host{display:block;--widget-renderer-radius: 8px;--widget-renderer-pill-radius: 999px;--widget-renderer-control-size: 28px;--widget-renderer-content-padding: 18px;--widget-renderer-shadow: 0 18px 40px #10182c38;--widget-renderer-transition-duration: .16s;--widget-renderer-transition-easing: ease;--widget-renderer-control-background: #10182ccc;--widget-renderer-control-border: #ffffff38;--widget-renderer-control-hover-border: #17233c;--widget-renderer-control-hover-background: #fff;--widget-renderer-control-hover-color: #17233c;--widget-renderer-action-background: #151515;--widget-renderer-action-color: #fff;--widget-renderer-action-outline-border: #737373;--widget-renderer-action-outline-background: #fff;--widget-renderer-action-outline-color: #151515}.widget-skeleton{position:relative;width:274px;max-width:100%;height:254px;box-sizing:border-box;overflow:hidden;border-radius:var(--widget-renderer-radius);background:#dfe5ee;box-shadow:var(--widget-renderer-shadow)}.widget-skeleton.is-wide{width:556px}.widget-skeleton.is-mini{width:133px;height:123px}.widget-skeleton.is-compact{width:274px;height:123px}.widget-skeleton.is-tall{width:133px;height:254px}.widget-skeleton.is-mega{width:556px;height:516px}.widget-skeleton.is-banner{width:274px;height:516px}.widget-skeleton.is-hero{width:1120px;height:516px}.widget-skeleton.is-mini .skeleton-toolbar{top:8px;right:8px;width:22px;height:22px}.widget-skeleton.is-mini .skeleton-content{box-sizing:border-box;height:100%;gap:3px;padding:24px 12px 8px}.widget-skeleton.is-mini .skeleton-line{height:6px}.widget-skeleton.is-mini .skeleton-line-title{height:10px}.widget-skeleton.is-mini .skeleton-chart{flex:1 1 auto;min-height:20px;margin-top:0}.widget-skeleton.is-mini .skeleton-button{width:42px;height:18px}.widget-skeleton:is(.is-compact,.is-tall) .skeleton-content{box-sizing:border-box;height:100%;gap:5px;padding:30px 12px 10px}.widget-skeleton.is-compact .skeleton-chart{min-height:28px;margin-top:0}.widget-skeleton.is-tall .skeleton-chart{flex:1 1 auto;min-height:45px;margin-top:0}.widget-skeleton:is(.is-mega,.is-banner,.is-hero) .skeleton-content{box-sizing:border-box;height:100%}.widget-skeleton:is(.is-mega,.is-banner,.is-hero) .skeleton-chart{flex:1 1 auto;min-height:180px}.widget-skeleton.is-dark{background:#182236}.skeleton-line,.skeleton-chart,.skeleton-button{background:linear-gradient(100deg,#dfe5ee 20%,#f2f5f9,#dfe5ee 70%);background-size:200% 100%;animation:skeleton-shimmer 1.8s linear infinite}.widget-skeleton.is-dark .skeleton-line,.widget-skeleton.is-dark .skeleton-chart,.widget-skeleton.is-dark .skeleton-button{background:linear-gradient(100deg,#182236 20%,#2b3b59,#182236 70%);background-size:200% 100%}.skeleton-toolbar{position:absolute;top:12px;right:12px;width:var(--widget-renderer-control-size);height:var(--widget-renderer-control-size);border-radius:var(--widget-renderer-radius);background:#c4cedd}.widget-skeleton.is-dark .skeleton-toolbar{background:#2b3b59}.skeleton-content{display:flex;flex-direction:column;gap:10px;padding:44px var(--widget-renderer-content-padding) 16px}.skeleton-line{display:block;height:10px;border-radius:var(--widget-renderer-pill-radius)}.skeleton-line-small{width:28%}.skeleton-line-title{width:72%;height:20px}.skeleton-line-copy{width:58%}.skeleton-chart{width:100%;height:64px;margin-top:2px;border-radius:var(--widget-renderer-radius)}.skeleton-button{align-self:flex-end;width:58px;height:30px;border-radius:var(--widget-renderer-radius)}@keyframes skeleton-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(prefers-reduced-motion:reduce){.skeleton-line,.skeleton-chart,.skeleton-button{animation:none}}.widget-shell{position:relative;overflow:visible;box-sizing:border-box;color:#f6f7fb}.widget-viewport{position:absolute;inset:0;overflow:hidden;border-radius:var(--widget-renderer-radius);box-shadow:var(--widget-renderer-shadow)}.widget-viewport:after{position:absolute;inset:0;z-index:5;box-sizing:border-box;border:1px solid #10182c26;border-radius:inherit;content:\"\";pointer-events:none}.widget-tooltip-layer{position:absolute;inset:0;z-index:6;overflow:visible;pointer-events:none}.chart-tooltip{--tooltip-border: rgb(0 0 0 / .12);position:absolute;z-index:4;top:var(--tooltip-y);left:var(--tooltip-x);display:flex;min-width:104px;align-items:center;gap:7px;padding:10px;transform:translate(-50%,calc(-100% - 10px));border:1px solid var(--tooltip-border);border-radius:var(--widget-renderer-radius, 4px);background:var(--widget-renderer-control-hover-background, #fff);box-shadow:1px 2px 10px #0003;color:#6e7079;font-size:14px;font-weight:400;line-height:1;white-space:nowrap}.chart-tooltip strong{margin-inline-start:auto;color:#464646;font-size:14px;font-weight:900}.tooltip-marker{width:9px;height:9px;flex:0 0 auto;border-radius:50%}.widget-grid{position:absolute;inset:0;display:grid;gap:8px;padding:18px}.chart-placeholder{width:100%;height:100%}.element-content{display:flex;width:100%;height:100%;min-width:0;min-height:0}.element-content:has(widget-chart-element){align-items:stretch}.element-content>widget-chart-element{flex:0 1 auto;width:100%;height:100%;max-width:100%;max-height:100%}.element-content>widget-chart-element.is-progress{height:auto}.divider-element{width:100%;height:2px;flex:0 0 auto;border-radius:var(--widget-renderer-pill-radius)}.divider-element.is-vertical{width:2px;height:100%}.element-content.is-sensitive{filter:blur(var(--sensitive-blur, 2px));-webkit-user-select:none;user-select:none}.text-element{display:inline-block;overflow:visible;line-height:1.2}img{width:100%;height:100%;display:block}.image-overlay{position:absolute;inset:0;z-index:1;pointer-events:none;border-radius:inherit}.image-placeholder{display:flex;width:100%;height:100%;align-items:center;justify-content:center;overflow:hidden;background:#dfe5ee}.image-placeholder svg{width:min(56px,45%);height:auto;fill:none;stroke:#8c9ab0;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}.action-button{min-height:30px;border-radius:var(--widget-renderer-radius);padding:6px 10px;font:600 12px/1.1 inherit;cursor:var(--widget-renderer-action-cursor, pointer)}.action-button.show-only-on-hover{opacity:0;pointer-events:none;transform:translateY(2px);transition:opacity var(--widget-renderer-transition-duration) var(--widget-renderer-transition-easing),transform var(--widget-renderer-transition-duration) var(--widget-renderer-transition-easing)}.widget-shell:hover .action-button.show-only-on-hover,.widget-shell:focus-within .action-button.show-only-on-hover{opacity:1;pointer-events:auto;transform:translateY(0)}.action-button.solid{border:1px solid #151515;background:#151515;color:#fff}.action-button.outline{border:1px solid #737373;background:#fff;color:#151515}\n"]
1347
1601
  }]
1348
1602
  }],
1349
1603
  ctorParameters: () => [],
@@ -1380,6 +1634,14 @@ i0.ɵɵngDeclareClassMetadata({
1380
1634
  required: false
1381
1635
  }]
1382
1636
  }],
1637
+ validationRules: [{
1638
+ type: i0.Input,
1639
+ args: [{
1640
+ isSignal: true,
1641
+ alias: "validationRules",
1642
+ required: false
1643
+ }]
1644
+ }],
1383
1645
  refreshRequested: [{
1384
1646
  type: i0.Output,
1385
1647
  args: ["refreshRequested"]
@@ -1430,6 +1692,6 @@ function widgetSizeOrSquare(value) {
1430
1692
  "hero"
1431
1693
  ].includes(value) ? value : "square";
1432
1694
  }
1433
- export { WIDGET_CELL_HEIGHT, WIDGET_CELL_WIDTH, WIDGET_GAP, WIDGET_HEIGHT, WIDGET_PADDING, WidgetRenderer, WidgetValidationError, chartAccessibilitySummary, chartAriaLabel, defaultWidgetRendererLocale, dutchWidgetRendererLocale, elementGridStyle, frenchWidgetRendererLocale, germanWidgetRendererLocale, gridSpanSize, gridTemplate, isWidget, isWidgetContract, normalizeWidgetInput, parseWidgetInput, safeParseWidgetInput, widgetFootprint, widgetHeight, widgetWidth };
1695
+ export { WIDGET_CELL_HEIGHT, WIDGET_CELL_WIDTH, WIDGET_FINGERPRINT_VERSION, WIDGET_GAP, WIDGET_HEIGHT, WIDGET_PADDING, WidgetRenderer, WidgetValidationError, chartAccessibilitySummary, chartAriaLabel, computeWidgetFingerprint, defaultWidgetRendererLocale, dutchWidgetRendererLocale, elementGridStyle, frenchWidgetRendererLocale, germanWidgetRendererLocale, gridSpanSize, gridTemplate, isWidget, isWidgetContract, normalizeWidgetInput, parseWidgetInput, safeParseWidgetInput, widgetFootprint, widgetHeight, widgetWidth };
1434
1696
 
1435
1697
  //# sourceMappingURL=lieselthuriot-widget-renderer.mjs.map