@aquera/nile-visualization 2.9.5 → 2.9.7

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.
@@ -421,6 +421,13 @@ export const styles = css `
421
421
  overflow-y: auto;
422
422
  -webkit-overflow-scrolling: touch;
423
423
  contain: none;
424
+ display: flex;
425
+ }
426
+
427
+ :host([fit]) .nile-chart-inner--kpi > nile-kpi-chart {
428
+ flex: 1 1 auto;
429
+ min-height: 0;
430
+ height: 100%;
424
431
  }
425
432
 
426
433
  .nile-chart-inner--filter {
@@ -459,19 +466,36 @@ export const styles = css `
459
466
  pointer-events: none;
460
467
  }
461
468
 
469
+ /* When loading, the card becomes a flex column that fills the host so the
470
+ skeleton can shrink with the container instead of forcing a fixed size. */
471
+ .nile-chart-card--loading {
472
+ display: flex;
473
+ flex-direction: column;
474
+ height: 100%;
475
+ }
476
+ .nile-chart-card--loading .nile-chart-wrapper {
477
+ flex: 1 1 auto;
478
+ min-height: 0;
479
+ }
480
+ .nile-chart-card--loading .nile-chart-inner {
481
+ height: 100%;
482
+ }
483
+
462
484
  .nile-chart-skeleton {
463
485
  display: flex;
464
486
  flex-direction: column;
465
487
  gap: 0;
466
488
  padding: var(--nile-spacing-3xl, var(--ng-spacing-3xl)) var(--nile-spacing-3xl, var(--ng-spacing-3xl)) var(--nile-spacing-xl, var(--ng-spacing-xl));
467
- min-height: var(--nile-chart-skeleton-height, 300px);
489
+ height: 100%;
490
+ box-sizing: border-box;
468
491
  }
469
492
 
470
493
  .nile-chart-skeleton-body {
471
494
  display: flex;
472
495
  flex-direction: column;
473
496
  justify-content: space-around;
474
- flex: 1;
497
+ flex: 1 1 auto;
498
+ min-height: 0;
475
499
  gap: var(--nile-spacing-14px, var(--ng-spacing-lg));
476
500
  padding-left: 44px;
477
501
  position: relative;
@@ -484,8 +508,8 @@ export const styles = css `
484
508
  left: 34px;
485
509
  top: var(--nile-spacing-xs, var(--ng-spacing-xs));
486
510
  bottom: var(--nile-spacing-xs, var(--ng-spacing-xs));
487
- width: var(--nile-border-width-2, var(--ng-stroke-width-2));
488
- border-radius: var(--nile-border-width-1, var(--ng-stroke-width-1));
511
+ width: 4px;
512
+ border-radius: 2px;
489
513
  background: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
490
514
  }
491
515
 
@@ -493,59 +517,398 @@ export const styles = css `
493
517
  display: flex;
494
518
  align-items: center;
495
519
  gap: var(--nile-spacing-md, var(--ng-spacing-md));
496
- height: var(--nile-spacing-3xl, var(--ng-spacing-3xl));
520
+ flex: 1 1 var(--nile-spacing-3xl, var(--ng-spacing-3xl));
521
+ min-height: 0;
522
+ max-height: var(--nile-spacing-3xl, var(--ng-spacing-3xl));
523
+ }
524
+
525
+ .nile-chart-skeleton-row .nile-chart-skeleton-ylabel-sk { flex-shrink: 0; }
526
+ /* Horizontal bar — uses <nile-skeleton-loader>. The host takes flex-basis
527
+ w% (inline style); ::part(base) forces the inner wrapper to fill the
528
+ host so the shimmer rectangle spans the full w% of the row. */
529
+ .nile-chart-skeleton-bar-sk::part(base) {
530
+ width: 100%;
531
+ height: 100%;
497
532
  }
533
+ .nile-chart-skeleton-row:nth-child(1) .nile-chart-skeleton-bar-rect { animation-delay: 0ms; }
534
+ .nile-chart-skeleton-row:nth-child(2) .nile-chart-skeleton-bar-rect { animation-delay: 100ms; }
535
+ .nile-chart-skeleton-row:nth-child(3) .nile-chart-skeleton-bar-rect { animation-delay: 200ms; }
536
+ .nile-chart-skeleton-row:nth-child(4) .nile-chart-skeleton-bar-rect { animation-delay: 300ms; }
537
+ .nile-chart-skeleton-row:nth-child(5) .nile-chart-skeleton-bar-rect { animation-delay: 400ms; }
498
538
 
499
- .nile-chart-skeleton-ylabel {
500
- width: var(--nile-height-26px, 26px);
501
- height: var(--nile-spacing-10px, var(--ng-spacing-md-alt));
502
- border-radius: var(--nile-radius-radius-sm, var(--ng-radius-xs));
539
+ /* Horizontal x-axis labels row */
540
+ .nile-chart-skeleton-xaxis-row {
541
+ display: flex;
542
+ justify-content: space-around;
543
+ padding-left: 44px;
544
+ margin-top: var(--nile-spacing-14px, var(--ng-spacing-lg));
503
545
  flex-shrink: 0;
546
+ min-width: 0;
547
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
548
+ }
549
+ .nile-chart-skeleton-xaxis-row nile-skeleton-loader { flex-shrink: 1; min-width: 0; }
550
+
551
+ /* Compact paddings + y-axis offset for narrow widths so the skeleton
552
+ doesn't overflow when the chart is shrunk. */
553
+ @container (max-width: 400px) {
554
+ .nile-chart-skeleton {
555
+ padding: var(--nile-spacing-xl, var(--ng-spacing-xl)) var(--nile-spacing-2xl, var(--ng-spacing-2xl)) var(--nile-spacing-lg, var(--ng-spacing-lg));
556
+ }
557
+ .nile-chart-skeleton-body { padding-left: 32px; }
558
+ .nile-chart-skeleton-body::before { left: 24px; }
559
+ .nile-chart-skeleton-xaxis-row { padding-left: 32px; }
560
+ }
561
+
562
+ @container (max-width: 280px) {
563
+ .nile-chart-skeleton {
564
+ padding: var(--nile-spacing-lg, var(--ng-spacing-lg)) var(--nile-spacing-xl, var(--ng-spacing-xl));
565
+ }
566
+ .nile-chart-skeleton-body { padding-left: 22px; }
567
+ .nile-chart-skeleton-body::before { left: 14px; }
568
+ .nile-chart-skeleton-xaxis-row { padding-left: 22px; }
569
+ .nile-chart-skeleton-ylabel { display: none; }
570
+ }
571
+
572
+ @keyframes nile-skeleton-blink {
573
+ 0%, 100% { opacity: 0.5; }
574
+ 50% { opacity: 1; }
575
+ }
576
+
577
+ /* ── Variant: columns (vertical bars) — wraps nile-skeleton-loader ── */
578
+ .nile-chart-skeleton-columns {
579
+ display: flex;
580
+ align-items: flex-end;
581
+ justify-content: space-between;
582
+ gap: var(--nile-spacing-md, var(--ng-spacing-md));
583
+ flex: 1 1 auto;
584
+ min-height: 0;
585
+ padding-left: 28px;
586
+ position: relative;
587
+ }
588
+ .nile-chart-skeleton-columns::before,
589
+ .nile-chart-skeleton-columns::after {
590
+ content: '';
591
+ position: absolute;
504
592
  background: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
505
- animation: nile-skeleton-blink 1.2s ease-in-out infinite;
593
+ border-radius: 1px;
506
594
  }
595
+ .nile-chart-skeleton-columns::before { left: 18px; top: 0; bottom: 0; width: 2px; }
596
+ .nile-chart-skeleton-columns::after { left: 18px; right: 0; bottom: 0; height: 2px; }
597
+ .nile-chart-skeleton-col-cell {
598
+ flex: 1 1 0;
599
+ min-width: 0;
600
+ height: 60%;
601
+ display: flex;
602
+ align-items: stretch;
603
+ border-radius: var(--nile-radius-radius-sm, var(--ng-radius-xs)) var(--nile-radius-radius-sm, var(--ng-radius-xs)) 0 0;
604
+ overflow: hidden;
605
+ }
606
+ .nile-chart-skeleton-col-cell nile-skeleton-loader {
607
+ width: 100%;
608
+ height: 100%;
609
+ display: block;
610
+ }
611
+ /* Percentage heights don't pass through the loader's shadow DOM by default.
612
+ Use ::part() to push height: 100% all the way to the shimmer rectangle. */
613
+ .nile-chart-skeleton-col-cell nile-skeleton-loader::part(base),
614
+ .nile-chart-skeleton-col-cell nile-skeleton-loader::part(skeleton),
615
+ .nile-chart-skeleton-heatmap-cell nile-skeleton-loader::part(base),
616
+ .nile-chart-skeleton-heatmap-cell nile-skeleton-loader::part(skeleton) {
617
+ height: 100%;
618
+ width: 100%;
619
+ }
620
+ .nile-chart-skeleton-xaxis-row--columns { padding-left: 28px; }
507
621
 
508
- .nile-chart-skeleton-bar {
509
- height: var(--nile-spacing-2xl, var(--ng-spacing-2xl));
510
- width: var(--w, 60%);
511
- border-radius: 0 var(--nile-radius-radius-sm, var(--ng-radius-xs)) var(--nile-radius-radius-sm, var(--ng-radius-xs)) 0;
622
+ /* ── Variant: line ── */
623
+ .nile-chart-skeleton-line {
624
+ flex: 1 1 auto;
625
+ min-height: 0;
626
+ padding-left: 28px;
627
+ position: relative;
628
+ }
629
+ .nile-chart-skeleton-line::before {
630
+ content: '';
631
+ position: absolute;
632
+ left: 18px; top: 0; bottom: 0;
633
+ width: 2px;
634
+ border-radius: 1px;
512
635
  background: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
636
+ }
637
+ .nile-chart-skeleton-line svg { width: 100%; height: 100%; display: block; }
638
+ .nile-chart-skeleton-line-area {
639
+ fill: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
640
+ opacity: 0.35;
641
+ animation: nile-skeleton-blink 1.6s ease-in-out infinite;
642
+ }
643
+ .nile-chart-skeleton-line-stroke {
644
+ fill: none;
645
+ stroke: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
646
+ stroke-width: 1.6;
647
+ stroke-linecap: round;
648
+ stroke-linejoin: round;
513
649
  animation: nile-skeleton-blink 1.2s ease-in-out infinite;
514
650
  }
515
651
 
516
- /* Staggered wave across the bars */
517
- .nile-chart-skeleton-row:nth-child(1) .nile-chart-skeleton-bar { animation-delay: 0ms; }
518
- .nile-chart-skeleton-row:nth-child(2) .nile-chart-skeleton-bar { animation-delay: 100ms; }
519
- .nile-chart-skeleton-row:nth-child(3) .nile-chart-skeleton-bar { animation-delay: 200ms; }
520
- .nile-chart-skeleton-row:nth-child(4) .nile-chart-skeleton-bar { animation-delay: 300ms; }
521
- .nile-chart-skeleton-row:nth-child(5) .nile-chart-skeleton-bar { animation-delay: 400ms; }
652
+ /* Trendline: faint guide line + actuals stroke + dashed forecast + split rule */
653
+ .nile-chart-skeleton-trend-line {
654
+ stroke: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
655
+ stroke-width: 0.8;
656
+ opacity: 0.45;
657
+ }
658
+ .nile-chart-skeleton-trend-forecast {
659
+ fill: none;
660
+ stroke: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
661
+ stroke-width: 1.6;
662
+ stroke-linecap: round;
663
+ stroke-dasharray: 3 3;
664
+ animation: nile-skeleton-blink 1.2s ease-in-out infinite;
665
+ animation-delay: 200ms;
666
+ }
667
+ .nile-chart-skeleton-trend-divider {
668
+ stroke: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
669
+ stroke-width: 0.5;
670
+ stroke-dasharray: 1 2;
671
+ opacity: 0.5;
672
+ }
522
673
 
523
- /* Horizontal x-axis labels row */
524
- .nile-chart-skeleton-xaxis-row {
674
+ /* Anomaly: halo pulses around outlier dots, dot itself blinks */
675
+ .nile-chart-skeleton-anomaly-dot {
676
+ fill: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
677
+ animation: nile-skeleton-blink 1.1s ease-in-out infinite;
678
+ }
679
+ .nile-chart-skeleton-anomaly-halo {
680
+ fill: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
681
+ opacity: 0.25;
682
+ transform-origin: center;
683
+ transform-box: fill-box;
684
+ animation: nile-skeleton-anomaly-pulse 1.8s ease-in-out infinite;
685
+ }
686
+ .nile-chart-skeleton-anomaly-halo:nth-of-type(1) { animation-delay: 0ms; }
687
+ .nile-chart-skeleton-anomaly-halo:nth-of-type(2) { animation-delay: 300ms; }
688
+ .nile-chart-skeleton-anomaly-halo:nth-of-type(3) { animation-delay: 600ms; }
689
+ @keyframes nile-skeleton-anomaly-pulse {
690
+ 0%, 100% { opacity: 0.15; transform: scale(0.7); }
691
+ 50% { opacity: 0.35; transform: scale(1.2); }
692
+ }
693
+
694
+ /* ── Variant: pie / donut ── */
695
+ .nile-chart-skeleton-pie-wrap {
525
696
  display: flex;
526
- justify-content: space-around;
527
- padding-left: 44px;
528
- margin-top: var(--nile-spacing-14px, var(--ng-spacing-lg));
697
+ align-items: center;
698
+ justify-content: center;
699
+ gap: var(--nile-spacing-3xl, var(--ng-spacing-3xl));
700
+ flex: 1 1 auto;
701
+ min-height: 0;
529
702
  }
703
+ .nile-chart-skeleton-pie {
704
+ width: min(60%, 220px);
705
+ aspect-ratio: 1;
706
+ border-radius: 50%;
707
+ background: conic-gradient(
708
+ var(--nile-colors-neutral-400, var(--ng-colors-border-secondary)) 0 25%,
709
+ var(--nile-colors-neutral-300, #e5e7eb) 25% 55%,
710
+ var(--nile-colors-neutral-400, var(--ng-colors-border-secondary)) 55% 80%,
711
+ var(--nile-colors-neutral-300, #e5e7eb) 80% 100%
712
+ );
713
+ mask: radial-gradient(circle, transparent 28%, black 30%);
714
+ -webkit-mask: radial-gradient(circle, transparent 28%, black 30%);
715
+ animation: nile-skeleton-spin 6s linear infinite, nile-skeleton-blink 1.6s ease-in-out infinite;
716
+ }
717
+ .nile-chart-skeleton-legend {
718
+ display: flex;
719
+ flex-direction: column;
720
+ gap: var(--nile-spacing-md, var(--ng-spacing-md));
721
+ flex-shrink: 0;
722
+ }
723
+ .nile-chart-skeleton-legend-row {
724
+ display: flex;
725
+ align-items: center;
726
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
727
+ }
728
+ @container (max-width: 320px) {
729
+ .nile-chart-skeleton-legend { display: none; }
730
+ }
731
+ @keyframes nile-skeleton-spin { to { transform: rotate(360deg); } }
530
732
 
531
- .nile-chart-skeleton-xlabel {
532
- height: var(--nile-spacing-10px, var(--ng-spacing-md-alt));
533
- width: var(--nile-height-30px, 30px);
534
- border-radius: var(--nile-radius-radius-sm, var(--ng-radius-xs));
733
+ /* ── Variant: scatter ── */
734
+ .nile-chart-skeleton-scatter {
735
+ flex: 1 1 auto;
736
+ min-height: 0;
737
+ position: relative;
738
+ padding-left: 28px;
739
+ }
740
+ .nile-chart-skeleton-scatter::before,
741
+ .nile-chart-skeleton-scatter::after {
742
+ content: '';
743
+ position: absolute;
535
744
  background: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
745
+ border-radius: 1px;
746
+ }
747
+ .nile-chart-skeleton-scatter::before { left: 18px; top: 0; bottom: 0; width: 2px; }
748
+ .nile-chart-skeleton-scatter::after { left: 18px; right: 0; bottom: 0; height: 2px; }
749
+ .nile-chart-skeleton-dot-pos {
750
+ position: absolute;
751
+ transform: translate(-50%, -50%);
752
+ display: inline-flex;
753
+ }
754
+
755
+ /* ── Variant: heatmap ── */
756
+ .nile-chart-skeleton-heatmap {
757
+ display: flex;
758
+ flex-direction: column;
759
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
760
+ flex: 1 1 auto;
761
+ min-height: 0;
762
+ }
763
+ .nile-chart-skeleton-heatmap-row {
764
+ display: flex;
765
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
766
+ flex: 1 1 0;
767
+ min-height: 0;
768
+ }
769
+ .nile-chart-skeleton-heatmap-cell {
770
+ flex: 1 1 0;
771
+ min-width: 0;
772
+ display: flex;
773
+ }
774
+ .nile-chart-skeleton-heatmap-cell nile-skeleton-loader { width: 100%; height: 100%; }
775
+
776
+ /* ── Variant: radar ── */
777
+ .nile-chart-skeleton-radar-wrap {
778
+ display: flex;
779
+ align-items: center;
780
+ justify-content: center;
781
+ flex: 1 1 auto;
782
+ min-height: 0;
783
+ }
784
+ .nile-chart-skeleton-radar-svg {
785
+ width: min(78%, 260px);
786
+ aspect-ratio: 1;
787
+ display: block;
788
+ }
789
+ .nile-chart-skeleton-radar-grid {
790
+ fill: none;
791
+ stroke: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
792
+ stroke-width: 0.8;
793
+ stroke-linejoin: round;
794
+ opacity: 0.7;
795
+ }
796
+ .nile-chart-skeleton-radar-spoke {
797
+ stroke: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
798
+ stroke-width: 0.6;
799
+ opacity: 0.5;
800
+ }
801
+ .nile-chart-skeleton-radar-data {
802
+ fill: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
803
+ stroke: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
804
+ stroke-width: 1.2;
805
+ stroke-linejoin: round;
806
+ opacity: 0.45;
807
+ animation: nile-skeleton-blink 1.4s ease-in-out infinite;
808
+ transform-origin: 50px 50px;
809
+ transform-box: fill-box;
810
+ }
811
+ .nile-chart-skeleton-radar-dot {
812
+ fill: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
536
813
  animation: nile-skeleton-blink 1.2s ease-in-out infinite;
537
- animation-delay: var(--d, 0ms);
538
814
  }
815
+ .nile-chart-skeleton-radar-dot:nth-child(5) { animation-delay: 0ms; }
816
+ .nile-chart-skeleton-radar-dot:nth-child(6) { animation-delay: 120ms; }
817
+ .nile-chart-skeleton-radar-dot:nth-child(7) { animation-delay: 240ms; }
818
+ .nile-chart-skeleton-radar-dot:nth-child(8) { animation-delay: 360ms; }
819
+ .nile-chart-skeleton-radar-dot:nth-child(9) { animation-delay: 480ms; }
539
820
 
540
- @keyframes nile-skeleton-blink {
541
- 0%, 100% { opacity: 0.5; }
542
- 50% { opacity: 1; }
821
+ /* ── Variant: timeline ── */
822
+ .nile-chart-skeleton-timeline {
823
+ display: flex;
824
+ flex-direction: column;
825
+ gap: var(--nile-spacing-md, var(--ng-spacing-md));
826
+ flex: 1 1 auto;
827
+ min-height: 0;
828
+ justify-content: space-around;
829
+ }
830
+ .nile-chart-skeleton-tl-row {
831
+ position: relative;
832
+ height: 12px;
833
+ }
834
+ .nile-chart-skeleton-tl-bar-pos {
835
+ position: absolute;
836
+ top: 0; bottom: 0;
837
+ display: inline-flex;
838
+ }
839
+
840
+ /* ── Variant: kpi ── */
841
+ .nile-chart-skeleton-kpi {
842
+ display: flex;
843
+ flex-direction: column;
844
+ gap: var(--nile-spacing-md, var(--ng-spacing-md));
845
+ flex: 1 1 auto;
846
+ min-height: 0;
847
+ justify-content: center;
848
+ }
849
+ .nile-chart-skeleton-kpi-spark {
850
+ width: 100%;
851
+ height: 40px;
852
+ margin-top: var(--nile-spacing-sm, var(--ng-spacing-sm));
853
+ }
854
+ .nile-chart-skeleton-kpi-spark path {
855
+ fill: none;
856
+ stroke: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
857
+ stroke-width: 1.6;
858
+ stroke-linecap: round;
859
+ stroke-linejoin: round;
860
+ animation: nile-skeleton-blink 1.2s ease-in-out infinite;
861
+ }
862
+
863
+ /* ── Variant: grid (table) ── */
864
+ .nile-chart-skeleton-grid {
865
+ display: flex;
866
+ flex-direction: column;
867
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
868
+ flex: 1 1 auto;
869
+ min-height: 0;
870
+ }
871
+ .nile-chart-skeleton-grid-head,
872
+ .nile-chart-skeleton-grid-row {
873
+ display: flex;
874
+ gap: var(--nile-spacing-md, var(--ng-spacing-md));
875
+ align-items: center;
876
+ flex: 1 1 0;
877
+ min-height: 0;
878
+ max-height: 28px;
879
+ }
880
+ .nile-chart-skeleton-grid-head {
881
+ border-bottom: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
882
+ padding-bottom: var(--nile-spacing-sm, var(--ng-spacing-sm));
883
+ }
884
+ .nile-chart-skeleton-grid-head nile-skeleton-loader,
885
+ .nile-chart-skeleton-grid-row nile-skeleton-loader {
886
+ flex: 1 1 0;
887
+ min-width: 0;
888
+ }
889
+
890
+ /* ── Variant: filter ── */
891
+ .nile-chart-skeleton-filter {
892
+ display: flex;
893
+ flex-direction: column;
894
+ gap: var(--nile-spacing-lg, var(--ng-spacing-lg));
895
+ flex: 1 1 auto;
896
+ min-height: 0;
897
+ justify-content: center;
898
+ }
899
+ .nile-chart-skeleton-filter-chips {
900
+ display: flex;
901
+ gap: var(--nile-spacing-sm, var(--ng-spacing-sm));
902
+ flex-wrap: wrap;
543
903
  }
544
904
 
545
905
  @media (prefers-reduced-motion: reduce) {
546
- .nile-chart-skeleton-bar,
547
- .nile-chart-skeleton-ylabel,
548
- .nile-chart-skeleton-xlabel {
906
+ .nile-chart-skeleton-line-area,
907
+ .nile-chart-skeleton-line-stroke,
908
+ .nile-chart-skeleton-pie,
909
+ .nile-chart-skeleton-radar-data,
910
+ .nile-chart-skeleton-radar-dot,
911
+ .nile-chart-skeleton-kpi-spark path {
549
912
  animation: none;
550
913
  opacity: 0.7;
551
914
  }
@@ -4,6 +4,7 @@ import { html, nothing } from 'lit';
4
4
  import NileElement from '../internal/nile-element.js';
5
5
  import { styles, tooltipCss } from './nile-chart.css.js';
6
6
  import { nileChartConfig } from './nile-chart-config-builder.js';
7
+ import { renderChartSkeleton } from './nile-chart-skeleton.js';
7
8
  import { convertConfig } from '../internal/chart-adapters.js';
8
9
  import { deepMerge } from '../internal/utils.js';
9
10
  import { initNileChartExporting, getHighcharts } from '../internal/highcharts-provider.js';
@@ -1710,27 +1711,13 @@ let NileChart = class NileChart extends NileElement {
1710
1711
  }
1711
1712
  }
1712
1713
  renderSkeleton() {
1713
- return html `
1714
- <div class="nile-chart-skeleton" aria-busy="true" aria-label="Loading chart">
1715
- <div class="nile-chart-skeleton-body">
1716
- ${[78, 55, 91, 42, 68].map(w => html `
1717
- <div class="nile-chart-skeleton-row">
1718
- <div class="nile-chart-skeleton-ylabel"></div>
1719
- <div class="nile-chart-skeleton-bar" style="--w: ${w}%"></div>
1720
- </div>
1721
- `)}
1722
- </div>
1723
- <div class="nile-chart-skeleton-xaxis-row">
1724
- ${[0, 1, 2, 3, 4].map(i => html `<div class="nile-chart-skeleton-xlabel" style="--d: ${i * 80}ms"></div>`)}
1725
- </div>
1726
- </div>
1727
- `;
1714
+ return renderChartSkeleton(this.activeConfig?.type);
1728
1715
  }
1729
1716
  render() {
1730
1717
  const isLoading = this.loading || (this.activeConfig?.loading ?? false);
1731
1718
  const isGrid = this.activeConfig?.type === 'grid';
1732
1719
  return html `
1733
- <div class="nile-chart-card ${isGrid ? 'nile-chart-card--grid' : ''}" part="chart-card">
1720
+ <div class="nile-chart-card ${isGrid ? 'nile-chart-card--grid' : ''} ${isLoading ? 'nile-chart-card--loading' : ''}" part="chart-card">
1734
1721
  ${this.renderHeader()}
1735
1722
  <div class="nile-chart-wrapper">
1736
1723
  <div class="nile-chart-inner ${this.activeConfig?.type === 'kpi' ? 'nile-chart-inner--kpi' : ''} ${this.activeConfig?.type === 'filter' ? 'nile-chart-inner--filter' : ''}" part="chart-inner">
@@ -490,9 +490,10 @@ export const styles = css `
490
490
  outline: none;
491
491
  background: transparent;
492
492
  border-radius: inherit;
493
- padding: var(--nile-spacing-lg, var(--ng-spacing-3)) var(--nile-spacing-lg, var(--ng-spacing-3));
493
+ padding: var(--nile-spacing-md, var(--ng-spacing-2)) var(--nile-spacing-xl, var(--ng-spacing-4));
494
494
  font-family: var(--nile-font-family-serif, var(--ng-font-family-body));
495
- font-size: var(--nile-font-size-sm);
495
+ font-size: var(--nile-type-scale-4, var(--ng-font-size-text-md));
496
+ font-weight: 400;
496
497
  color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));
497
498
  line-height: 1.4;
498
499
  box-sizing: border-box;
@@ -605,18 +606,14 @@ export const styles = css `
605
606
  color: transparent;
606
607
  }
607
608
 
608
- /* ── Custom suggestion dropdown ─── */
609
- /* Hidden by default; shown only when fc-prompt__inner has focus-within.*/
610
- /* Mousedown on a suggestion preventDefaults to keep input focus, so */
611
- /* the dropdown stays open across picks. Click outside → focus-within */
612
- /* fails → dropdown hides. */
613
- .fc-prompt__suggestions {
614
- display: none;
615
- position: absolute;
616
- top: calc(100% + 6px);
617
- left: 0;
618
- right: 0;
619
- z-index: 20;
609
+ .fc-prompt__dropdown {
610
+ flex: 1;
611
+ min-width: 0;
612
+ display: block;
613
+ }
614
+
615
+ .fc-prompt__dropdown::part(panel) {
616
+ display: flex;
620
617
  flex-direction: column;
621
618
  max-height: 280px;
622
619
  overflow-y: auto;
@@ -628,10 +625,6 @@ export const styles = css `
628
625
  padding: var(--nile-spacing-4px, var(--ng-spacing-1));
629
626
  }
630
627
 
631
- .fc-prompt__inner:focus-within .fc-prompt__suggestions {
632
- display: flex;
633
- }
634
-
635
628
  .fc-prompt__suggestion {
636
629
  display: flex;
637
630
  align-items: center;
@@ -18,6 +18,9 @@ export declare class NileFilterChart extends NileElement implements FilterChartH
18
18
  promptModes: Map<string, PromptMode>;
19
19
  /** Highlighted suggestion index per prompt control id (-1 = none highlighted). */
20
20
  promptActiveIndex: Map<string, number>;
21
+ /** Id of the prompt control whose input currently has focus (drives the
22
+ * nile-dropdown panel open state). null when no prompt is focused. */
23
+ promptFocusedId: string | null;
21
24
  /** Active typewriter timers per prompt control id (so we can stop them). */
22
25
  private _promptTimers;
23
26
  /** Compiled filtrex predicates per prompt control id (strict-mode successes). */
@@ -33,6 +36,7 @@ export declare class NileFilterChart extends NileElement implements FilterChartH
33
36
  connectedCallback(): void;
34
37
  disconnectedCallback(): void;
35
38
  updated(changed: Map<string, unknown>): void;
39
+ private _syncPortalDropdowns;
36
40
  setValue(id: string, value: unknown): void;
37
41
  /**
38
42
  * Update a prompt control's value. The value lands in `selectedValues`
@@ -55,6 +59,7 @@ export declare class NileFilterChart extends NileElement implements FilterChartH
55
59
  */
56
60
  submitPrompt(ctrl: NormalizedFilterControl): void;
57
61
  setPromptActiveIndex(id: string, idx: number): void;
62
+ setPromptFocused(id: string, focused: boolean): void;
58
63
  private _validateOrClear;
59
64
  private _clearPromptValidation;
60
65
  private _syncPromptAnimations;
@@ -34,6 +34,9 @@ let NileFilterChart = NileFilterChart_1 = class NileFilterChart extends NileElem
34
34
  this.promptModes = new Map();
35
35
  /** Highlighted suggestion index per prompt control id (-1 = none highlighted). */
36
36
  this.promptActiveIndex = new Map();
37
+ /** Id of the prompt control whose input currently has focus (drives the
38
+ * nile-dropdown panel open state). null when no prompt is focused. */
39
+ this.promptFocusedId = null;
37
40
  /** Active typewriter timers per prompt control id (so we can stop them). */
38
41
  this._promptTimers = new Map();
39
42
  /** Compiled filtrex predicates per prompt control id (strict-mode successes). */
@@ -69,6 +72,20 @@ let NileFilterChart = NileFilterChart_1 = class NileFilterChart extends NileElem
69
72
  this._initValues();
70
73
  this._syncPromptAnimations();
71
74
  }
75
+ // Keep portaled suggestion panels in sync with the latest panel content.
76
+ // nile-dropdown clones the panel once at open-time and never re-clones,
77
+ // so without this the suggestions appear frozen at the value they had
78
+ // when the dropdown first opened.
79
+ this._syncPortalDropdowns();
80
+ }
81
+ _syncPortalDropdowns() {
82
+ const dropdowns = this.shadowRoot?.querySelectorAll('nile-dropdown.fc-prompt__dropdown');
83
+ dropdowns?.forEach((dd) => {
84
+ const d = dd;
85
+ if (d.open && d.portal && d.portalManager?.clonedPanel) {
86
+ d.portalManager.updatePortalPanel?.();
87
+ }
88
+ });
72
89
  }
73
90
  // ── FilterChartHost surface ─────────────────────────────────────────────────
74
91
  setValue(id, value) {
@@ -127,6 +144,14 @@ let NileFilterChart = NileFilterChart_1 = class NileFilterChart extends NileElem
127
144
  setPromptActiveIndex(id, idx) {
128
145
  this.promptActiveIndex = new Map(this.promptActiveIndex).set(id, idx);
129
146
  }
147
+ setPromptFocused(id, focused) {
148
+ if (focused) {
149
+ this.promptFocusedId = id;
150
+ }
151
+ else if (this.promptFocusedId === id) {
152
+ this.promptFocusedId = null;
153
+ }
154
+ }
130
155
  _validateOrClear(ctrl, value, opts = {}) {
131
156
  const isNql = this.promptModes.get(ctrl.id) === 'nql';
132
157
  if (value.trim() === '') {
@@ -566,6 +591,9 @@ __decorate([
566
591
  __decorate([
567
592
  state()
568
593
  ], NileFilterChart.prototype, "promptActiveIndex", void 0);
594
+ __decorate([
595
+ state()
596
+ ], NileFilterChart.prototype, "promptFocusedId", void 0);
569
597
  NileFilterChart = NileFilterChart_1 = __decorate([
570
598
  customElement('nile-filter-chart')
571
599
  ], NileFilterChart);