@aquera/nile-visualization 2.2.0 → 2.3.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.
@@ -92,6 +92,8 @@ export { NileSpiderwebChart } from './nile-spiderweb-chart/index.js';
92
92
  export type { SpiderwebChartSeriesData } from './nile-spiderweb-chart/index.js';
93
93
  export { NileKpiChart } from './nile-kpi-chart/index.js';
94
94
  export type { ChartKpiSeparatedPayload, KpiConfig, NileKpiConfigInputType, TrendDirection, KpiVariant, } from './nile-kpi-chart/index.js';
95
+ export { NileFilterChart } from './nile-filter-chart/index.js';
96
+ export type { FilterChartSeparatedPayload, FilterControl, FilterOption, FilterChartConfig, } from './nile-filter-chart/nile-filter-chart.js';
95
97
  export { NileMapChart } from './nile-map-chart/index.js';
96
98
  export type { MapChartDataPoint } from './nile-map-chart/index.js';
97
99
  export { NileAiSender } from './nile-ai-sender/index.js';
package/dist/src/index.js CHANGED
@@ -49,6 +49,7 @@ export { NileHeatmapChart } from './nile-heatmap-chart/index.js';
49
49
  export { NileFlameChart } from './nile-flame-chart/index.js';
50
50
  export { NileSpiderwebChart } from './nile-spiderweb-chart/index.js';
51
51
  export { NileKpiChart } from './nile-kpi-chart/index.js';
52
+ export { NileFilterChart } from './nile-filter-chart/index.js';
52
53
  export { NileMapChart } from './nile-map-chart/index.js';
53
54
  export { NileAiSender } from './nile-ai-sender/index.js';
54
55
  export { NileAiPanel } from './nile-ai-panel/index.js';
@@ -0,0 +1,6 @@
1
+ import type { FilterChartConfig, FilterControl, FilterOption } from '../../nile-filter-chart/nile-filter-chart.js';
2
+ /** `chart` slice for `type: 'filter'` — use with `<nile-chart>` or `{ chart, aq }` on `<nile-filter-chart>`. */
3
+ export type ChartFilterConfigType = FilterChartConfig;
4
+ /** Filter fields without the `type` discriminator. */
5
+ export type ChartFilterPropsType = Omit<FilterChartConfig, 'type'>;
6
+ export type { FilterControl, FilterOption };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=chart-filter-config.type.js.map
@@ -13,6 +13,7 @@ import type { ChartHeatmapConfigType } from './chart-heatmap-config.type.js';
13
13
  import type { ChartFlameConfigType } from './chart-flame-config.type.js';
14
14
  import type { ChartSpiderwebConfigType } from './chart-spiderweb-config.type.js';
15
15
  import type { ChartKpiConfigType } from './chart-kpi-config.type.js';
16
+ import type { ChartFilterConfigType } from './chart-filter-config.type.js';
16
17
  import type { ChartInvertedAreaConfigType } from './chart-area-config.type.js';
17
18
  import type { ChartColumnPyramidConfigType } from './chart-column-pyramid-config.type.js';
18
19
  import type { ChartLollipopConfigType } from './chart-lollipop-config.type.js';
@@ -33,4 +34,4 @@ import type { ChartXrangeConfigType } from './chart-xrange-config.type.js';
33
34
  * Chart configs for primitive `<nile-*-chart>` elements (`el.config = { chart, aq }`).
34
35
  * Discriminated on `chart.type`.
35
36
  */
36
- export type PrimitiveChartConfigType = ChartInvertedAreaConfigType | ChartColumnPyramidConfigType | ChartLollipopConfigType | ChartAreaSplineConfigType | ChartAreaNegativeConfigType | ChartAreaRangeConfigType | ChartColumnRangeConfigType | ChartColumnDrilldownConfigType | ChartRadialBarConfigType | ChartVariablePieConfigType | ChartDumbbellLowerConfigType | ChartDumbbellUpperConfigType | ChartEulerConfigType | ChartPolygonConfigType | ChartVectorConfigType | ChartXrangeConfigType | ChartClusterConfigType | ChartStackedConfigType | ChartHistogramConfigType | ChartBellcurveConfigType | ChartBoxplotConfigType | ChartTimelineConfigType | ChartDumbbellConfigType | ChartFanConfigType | ChartFunnelConfigType | ChartOrganizationConfigType | ChartLineColumnConfigType | ChartHeatmapConfigType | ChartFlameConfigType | ChartSpiderwebConfigType | ChartKpiConfigType;
37
+ export type PrimitiveChartConfigType = ChartInvertedAreaConfigType | ChartColumnPyramidConfigType | ChartLollipopConfigType | ChartAreaSplineConfigType | ChartAreaNegativeConfigType | ChartAreaRangeConfigType | ChartColumnRangeConfigType | ChartColumnDrilldownConfigType | ChartRadialBarConfigType | ChartVariablePieConfigType | ChartDumbbellLowerConfigType | ChartDumbbellUpperConfigType | ChartEulerConfigType | ChartPolygonConfigType | ChartVectorConfigType | ChartXrangeConfigType | ChartClusterConfigType | ChartStackedConfigType | ChartHistogramConfigType | ChartBellcurveConfigType | ChartBoxplotConfigType | ChartTimelineConfigType | ChartDumbbellConfigType | ChartFanConfigType | ChartFunnelConfigType | ChartOrganizationConfigType | ChartLineColumnConfigType | ChartHeatmapConfigType | ChartFlameConfigType | ChartSpiderwebConfigType | ChartKpiConfigType | ChartFilterConfigType;
@@ -536,5 +536,11 @@ export interface NileKpiChartConfig extends NileChartConfigBase {
536
536
  numberSystem?: KpiNumberSystem;
537
537
  tooltipEnabled?: boolean;
538
538
  }
539
+ export type { FilterOption, FilterControl } from '../nile-filter-chart/nile-filter-chart.js';
540
+ import type { FilterControl } from '../nile-filter-chart/nile-filter-chart.js';
541
+ export interface NileFilterChartConfig extends NileChartConfigBase {
542
+ type: 'filter';
543
+ controls: FilterControl[];
544
+ }
539
545
  /** Discriminated union of every named chart config — use for typed `<nile-chart>` configs. */
540
- export type NileChartConfig = NileBarChartConfig | NilePieChartConfig | NileLineChartConfig | NileAreaChartConfig | NileColumnChartConfig | NileDonutChartConfig | NileScatterChartConfig | NileBubbleChartConfig | NileSplineChartConfig | NileRadarChartConfig | NileGaugeChartConfig | NileWaterfallChartConfig | NileMapChartConfig | NileGridChartConfig | NileTrendlineChartConfig | NileAnomalyChartConfig | NileHistogramChartConfig | NileBellcurveChartConfig | NileBoxplotChartConfig | NileStackedChartConfig | NileClusterChartConfig | NileColumnPyramidChartConfig | NileColumnRangeChartConfig | NileColumnDrilldownChartConfig | NileLollipopChartConfig | NileInvertedAreaChartConfig | NileAreaSplineChartConfig | NileAreaNegativeChartConfig | NileAreaRangeChartConfig | NileDumbbellChartConfig | NileDumbbellLowerChartConfig | NileDumbbellUpperChartConfig | NileRadialBarChartConfig | NileSpiderwebChartConfig | NileTimelineChartConfig | NileFanChartConfig | NileFunnelChartConfig | NileOrganizationChartConfig | NileHeatmapChartConfig | NileFlameChartConfig | NileVariablePieChartConfig | NileEulerChartConfig | NilePolygonChartConfig | NileVectorChartConfig | NileXrangeChartConfig | NileLineColumnChartConfig | NileKpiChartConfig;
546
+ export type NileChartConfig = NileBarChartConfig | NilePieChartConfig | NileLineChartConfig | NileAreaChartConfig | NileColumnChartConfig | NileDonutChartConfig | NileScatterChartConfig | NileBubbleChartConfig | NileSplineChartConfig | NileRadarChartConfig | NileGaugeChartConfig | NileWaterfallChartConfig | NileMapChartConfig | NileGridChartConfig | NileTrendlineChartConfig | NileAnomalyChartConfig | NileHistogramChartConfig | NileBellcurveChartConfig | NileBoxplotChartConfig | NileStackedChartConfig | NileClusterChartConfig | NileColumnPyramidChartConfig | NileColumnRangeChartConfig | NileColumnDrilldownChartConfig | NileLollipopChartConfig | NileInvertedAreaChartConfig | NileAreaSplineChartConfig | NileAreaNegativeChartConfig | NileAreaRangeChartConfig | NileDumbbellChartConfig | NileDumbbellLowerChartConfig | NileDumbbellUpperChartConfig | NileRadialBarChartConfig | NileSpiderwebChartConfig | NileTimelineChartConfig | NileFanChartConfig | NileFunnelChartConfig | NileOrganizationChartConfig | NileHeatmapChartConfig | NileFlameChartConfig | NileVariablePieChartConfig | NileEulerChartConfig | NilePolygonChartConfig | NileVectorChartConfig | NileXrangeChartConfig | NileLineColumnChartConfig | NileKpiChartConfig | NileFilterChartConfig;
@@ -417,6 +417,10 @@ export const styles = css `
417
417
  contain: none;
418
418
  }
419
419
 
420
+ .nile-chart-inner--filter {
421
+ contain: none;
422
+ }
423
+
420
424
  .nile-ai-trigger.active {
421
425
  background: var(--ng-componentcolors-utility-brand-100, #DBE8FF);
422
426
  }
@@ -48,6 +48,7 @@ import '../nile-polygon-chart/index.js';
48
48
  import '../nile-vector-chart/index.js';
49
49
  import '../nile-xrange-chart/index.js';
50
50
  import '../nile-kpi-chart/index.js';
51
+ import '../nile-filter-chart/index.js';
51
52
  import '@aquera/nile-data-grid';
52
53
  import '../nile-ai-panel/index.js';
53
54
  export declare class NileChart extends NileElement {
@@ -59,6 +59,7 @@ import '../nile-polygon-chart/index.js';
59
59
  import '../nile-vector-chart/index.js';
60
60
  import '../nile-xrange-chart/index.js';
61
61
  import '../nile-kpi-chart/index.js';
62
+ import '../nile-filter-chart/index.js';
62
63
  import '@aquera/nile-data-grid';
63
64
  import '../nile-ai-panel/index.js';
64
65
  const CORE_CHART_LABELS = {
@@ -1672,6 +1673,12 @@ let NileChart = class NileChart extends NileElement {
1672
1673
  .noMatchMessage=${config.noMatchMessage ?? 'No matching rows'}
1673
1674
  style=${gridChrome}
1674
1675
  ></nile-data-grid>`;
1676
+ }
1677
+ case 'filter': {
1678
+ return html `<nile-filter-chart
1679
+ .config=${{ chart: config }}
1680
+ @nile-change="${(e) => this.emit('nile-change', e.detail)}"
1681
+ ></nile-filter-chart>`;
1675
1682
  }
1676
1683
  default: {
1677
1684
  const _exhaustive = config;
@@ -1703,7 +1710,7 @@ let NileChart = class NileChart extends NileElement {
1703
1710
  <div class="nile-chart-card ${isGrid ? 'nile-chart-card--grid' : ''}">
1704
1711
  ${this.renderHeader()}
1705
1712
  <div class="nile-chart-wrapper">
1706
- <div class="nile-chart-inner ${this.activeConfig?.type === 'kpi' ? 'nile-chart-inner--kpi' : ''}">
1713
+ <div class="nile-chart-inner ${this.activeConfig?.type === 'kpi' ? 'nile-chart-inner--kpi' : ''} ${this.activeConfig?.type === 'filter' ? 'nile-chart-inner--filter' : ''}">
1707
1714
  ${isLoading
1708
1715
  ? this.renderSkeleton()
1709
1716
  : this.activeConfig
@@ -0,0 +1,2 @@
1
+ export { NileFilterChart } from './nile-filter-chart.js';
2
+ export type { FilterChartSeparatedPayload } from './nile-filter-chart.js';
@@ -0,0 +1,2 @@
1
+ export { NileFilterChart } from './nile-filter-chart.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,361 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host { display: block; width: 100%; }
4
+
5
+ .fc-root {
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: var(--nile-spacing-none, var(--ng-spacing-0));
9
+ }
10
+
11
+ /* ── Group ─── */
12
+ .fc-group {
13
+ border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
14
+ border-radius: var(--nile-radius-md, var(--ng-radius-md));
15
+ overflow: hidden;
16
+ }
17
+
18
+ .fc-group__header {
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: space-between;
22
+ padding: var(--nile-spacing-lg, var(--ng-spacing-3)) var(--nile-spacing-2xl, var(--ng-spacing-5));
23
+ background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));
24
+ border-bottom: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
25
+ gap: var(--nile-spacing-8px, var(--ng-spacing-2));
26
+ }
27
+
28
+ .fc-group__header--collapsible {
29
+ cursor: pointer;
30
+ user-select: none;
31
+ }
32
+
33
+ .fc-group__header--collapsible:hover {
34
+ background: var(--nile-colors-dark-200, var(--ng-colors-bg-secondary-hover));
35
+ }
36
+
37
+ .fc-group__header-text {
38
+ display: flex;
39
+ flex-direction: column;
40
+ gap: var(--nile-spacing-2px, var(--ng-spacing-0-5));
41
+ }
42
+
43
+ .fc-group__label {
44
+ font-size: var(--nile-font-size-sm, 13px);
45
+ font-weight: var(--nile-font-weight-bold, var(--ng-font-weight-700));
46
+ color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));
47
+ letter-spacing: 0.02em;
48
+ }
49
+
50
+ .fc-group__desc {
51
+ font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));
52
+ color: var(--nile-colors-neutral-700, var(--ng-colors-text-quaternary-500));
53
+ line-height: 1.4;
54
+ }
55
+
56
+ .fc-group__chevron {
57
+ font-size: var(--nile-type-scale-5, var(--ng-font-size-text-lg));
58
+ font-weight: var(--ng-font-weight-300, 300);
59
+ color: var(--nile-colors-neutral-700, var(--ng-colors-text-quaternary-500));
60
+ transition: transform 0.2s ease;
61
+ transform: rotate(0deg);
62
+ flex-shrink: 0;
63
+ }
64
+
65
+ .fc-group__chevron--open {
66
+ transform: rotate(90deg);
67
+ }
68
+
69
+ .fc-group__body {
70
+ display: flex;
71
+ flex-direction: column;
72
+ }
73
+
74
+ .fc-group__body .fc-control {
75
+ border-bottom: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
76
+ }
77
+
78
+ .fc-group__body .fc-control:last-child {
79
+ border-bottom: none;
80
+ }
81
+
82
+ /* ── Control card ─── */
83
+ .fc-control {
84
+ padding: var(--nile-spacing-xl, var(--ng-spacing-4)) var(--nile-spacing-2xl, var(--ng-spacing-5));
85
+ }
86
+
87
+ .fc-control__label {
88
+ font-size: var(--nile-font-size-sm, 13px);
89
+ font-weight: var(--nile-font-weight-semi-bold, var(--ng-font-weight-600));
90
+ color: var(--nile-colors-neutral-700, var(--ng-colors-text-tertiary-600));
91
+ text-transform: uppercase;
92
+ letter-spacing: 0.05em;
93
+ margin-bottom: var(--nile-spacing-4px, var(--ng-spacing-1));
94
+ }
95
+
96
+ .fc-control__desc {
97
+ font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));
98
+ color: var(--nile-colors-neutral-700, var(--ng-colors-text-quaternary-500));
99
+ margin-bottom: var(--nile-spacing-lg, var(--ng-spacing-3));
100
+ line-height: 1.4;
101
+ }
102
+
103
+ .fc-control__body {
104
+ display: flex;
105
+ flex-direction: column;
106
+ gap: var(--nile-spacing-8px, var(--ng-spacing-2));
107
+ }
108
+
109
+ /* ── Badge ─── */
110
+ .fc-badge-group {
111
+ display: flex;
112
+ flex-wrap: wrap;
113
+ gap: var(--nile-spacing-6px, var(--ng-spacing-1-5));
114
+ }
115
+
116
+ .fc-badge {
117
+ display: inline-flex;
118
+ align-items: center;
119
+ gap: var(--nile-spacing-6px, var(--ng-spacing-1-5));
120
+ padding: var(--nile-spacing-4px, var(--ng-spacing-1)) var(--nile-spacing-14px, var(--ng-spacing-3-5));
121
+ border-radius: var(--nile-radius-full, var(--ng-radius-full));
122
+ font-size: var(--nile-font-size-sm, 13px);
123
+ font-weight: var(--nile-font-weight-medium, var(--ng-font-weight-500));
124
+ cursor: pointer;
125
+ border: 1.5px solid var(--nile-colors-neutral-500, var(--ng-colors-border-primary));
126
+ background: var(--nile-colors-white-base, var(--ng-color-base-white));
127
+ color: var(--nile-colors-dark-500, var(--ng-colors-text-secondary-700));
128
+ transition: background 0.12s, border-color 0.12s, color 0.12s;
129
+ white-space: nowrap;
130
+ line-height: 1.4;
131
+ }
132
+
133
+ .fc-badge:hover {
134
+ border-color: var(--nile-colors-primary-400, var(--ng-color-bluedark-400));
135
+ background: var(--nile-colors-primary-100, var(--ng-colors-bg-brand-primary));
136
+ }
137
+
138
+ .fc-badge--selected {
139
+ border-color: var(--nile-colors-primary-600, var(--ng-colors-bg-brand-solid));
140
+ background: var(--nile-colors-primary-600, var(--ng-colors-bg-brand-solid));
141
+ color: var(--nile-colors-white-base, var(--ng-color-base-white));
142
+ }
143
+
144
+ .fc-badge--selected .fc-dot { border-color: var(--nile-colors-white-500, var(--ng-color-graydarkmodealpha-500)); }
145
+
146
+ .fc-dot {
147
+ width: var(--nile-spacing-8px, var(--ng-spacing-2));
148
+ height: var(--nile-spacing-8px, var(--ng-spacing-2));
149
+ border-radius: var(--nile-radius-full, var(--ng-radius-full));
150
+ flex-shrink: 0;
151
+ border: 1.5px solid var(--nile-colors-transparent, var(--ng-color-base-transparent));
152
+ }
153
+
154
+ /* ── Slider ─── */
155
+ .fc-slider-wrap { display: flex; flex-direction: column; gap: var(--nile-spacing-10px, var(--ng-spacing-2-5)); }
156
+
157
+ .fc-slider-label {
158
+ display: flex;
159
+ justify-content: space-between;
160
+ font-size: var(--nile-font-size-sm, 13px);
161
+ color: var(--nile-colors-neutral-700, var(--ng-colors-text-tertiary-600));
162
+ }
163
+
164
+ .fc-slider-range strong { color: var(--nile-colors-primary-600, var(--ng-color-bluedark-600)); }
165
+
166
+ nile-tag { cursor: pointer; }
167
+
168
+ /* Expand slider to fill the card width */
169
+ nile-slider { display: block; width: 100%; }
170
+
171
+ nile-slider::part(base) {
172
+ height: auto;
173
+ padding: var(--nile-spacing-4px, var(--ng-spacing-1)) var(--nile-spacing-none, var(--ng-spacing-0));
174
+ }
175
+
176
+ nile-slider::part(range-container) {
177
+ flex: 1;
178
+ width: 100%;
179
+ min-width: 0;
180
+ }
181
+
182
+ /* Make the track fill the container and be easier to see */
183
+ nile-slider::part(range) {
184
+ width: 100%;
185
+ height: var(--nile-height-6px, var(--ng-spacing-1-5));
186
+ }
187
+
188
+ /* The filled portion between the two handles */
189
+ nile-slider::part(range-completed) {
190
+ background-color: var(--nile-colors-primary-600, var(--ng-color-bluedark-600));
191
+ height: 100%;
192
+ }
193
+
194
+ /* ── Tree ─── */
195
+ /* nile-tree sets font-size:0 on :host for em-based indentation maths.
196
+ Without --nile-type-scale-4 defined the label inherits 0 and becomes invisible.
197
+ We also enable indent guide lines and fix the indent step size. */
198
+ nile-tree {
199
+ --nile-type-scale-4: var(--nile-font-size-sm, 13px);
200
+ --indent-size: 0.875rem;
201
+ --indent-guide-width: 1px;
202
+ --indent-guide-style: solid;
203
+ --indent-guide-color: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
204
+ }
205
+
206
+ /* ── Segmented ─── */
207
+ .fc-segmented-scroll {
208
+ overflow-x: auto;
209
+ overflow-y: hidden;
210
+ -webkit-overflow-scrolling: touch;
211
+ scrollbar-width: none;
212
+ }
213
+
214
+ .fc-segmented-scroll::-webkit-scrollbar { display: none; }
215
+
216
+ .fc-segmented-scroll nile-button-toggle-group {
217
+ display: inline-flex;
218
+ white-space: nowrap;
219
+ min-width: max-content;
220
+ }
221
+
222
+ /* ── Toggle ─── */
223
+ .fc-toggle-group {
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: var(--nile-spacing-10px, var(--ng-spacing-2-5));
227
+ }
228
+
229
+
230
+ /* ── Comparison ─── */
231
+ .fc-comparison {
232
+ display: flex;
233
+ align-items: center;
234
+ gap: var(--nile-spacing-10px, var(--ng-spacing-2-5));
235
+ }
236
+
237
+ .fc-comparison nile-select { flex: 1; }
238
+
239
+ .fc-vs {
240
+ flex-shrink: 0;
241
+ font-size: 11px;
242
+ font-weight: var(--nile-font-weight-bold, var(--ng-font-weight-700));
243
+ color: var(--nile-colors-white-base, var(--ng-color-base-white));
244
+ background: var(--nile-colors-neutral-700, var(--ng-color-graylightmode-700));
245
+ border-radius: var(--nile-radius-sm, var(--ng-radius-xs));
246
+ padding: 3px var(--nile-spacing-6px, var(--ng-spacing-1-5));
247
+ letter-spacing: 0.05em;
248
+ }
249
+
250
+ /* ── Threshold ─── */
251
+ .fc-threshold {
252
+ display: flex;
253
+ flex-direction: column;
254
+ gap: var(--nile-spacing-lg, var(--ng-spacing-3));
255
+ }
256
+
257
+ .fc-threshold-metric-row {
258
+ display: flex;
259
+ align-items: flex-end;
260
+ gap: var(--nile-spacing-10px, var(--ng-spacing-2-5));
261
+ }
262
+
263
+ .fc-threshold-cond-row {
264
+ display: flex;
265
+ align-items: flex-end;
266
+ gap: var(--nile-spacing-lg, var(--ng-spacing-3));
267
+ padding-left: var(--nile-spacing-4px, var(--ng-spacing-1));
268
+ }
269
+
270
+ .fc-threshold-field {
271
+ display: flex;
272
+ flex-direction: column;
273
+ gap: var(--nile-spacing-6px, var(--ng-spacing-1-5));
274
+ }
275
+
276
+ .fc-threshold-field-label {
277
+ font-size: 11px;
278
+ font-weight: var(--nile-font-weight-semi-bold, var(--ng-font-weight-600));
279
+ text-transform: uppercase;
280
+ letter-spacing: 0.06em;
281
+ color: var(--nile-colors-neutral-700, var(--ng-colors-text-quaternary-500));
282
+ }
283
+
284
+ .fc-threshold-field--metric { flex: 1; }
285
+ .fc-threshold-field--op { flex: 0 0 150px; }
286
+ .fc-threshold-field--val { flex: 1; }
287
+
288
+ .fc-threshold-where {
289
+ flex-shrink: 0;
290
+ align-self: flex-end;
291
+ margin-bottom: var(--nile-spacing-6px, var(--ng-spacing-1-5));
292
+ font-size: 11px;
293
+ font-weight: var(--nile-font-weight-bold, var(--ng-font-weight-700));
294
+ letter-spacing: 0.06em;
295
+ color: var(--nile-colors-white-base, var(--ng-color-base-white));
296
+ background: var(--nile-colors-dark-500, var(--ng-color-graylightmode-600));
297
+ border-radius: var(--nile-radius-sm, var(--ng-radius-xs));
298
+ padding: var(--nile-spacing-4px, var(--ng-spacing-1)) var(--nile-spacing-10px, var(--ng-spacing-2-5));
299
+ }
300
+
301
+ .fc-threshold-preview {
302
+ display: flex;
303
+ align-items: center;
304
+ gap: var(--nile-spacing-6px, var(--ng-spacing-1-5));
305
+ padding: var(--nile-spacing-8px, var(--ng-spacing-2)) var(--nile-spacing-lg, var(--ng-spacing-3));
306
+ border-radius: var(--nile-radius-radius-lg, var(--ng-radius-sm));
307
+ background: var(--nile-colors-primary-100, var(--ng-colors-bg-brand-primary));
308
+ border: 1px solid var(--nile-colors-primary-400, var(--ng-color-bluedark-200));
309
+ font-size: var(--nile-font-size-sm, 13px);
310
+ color: var(--nile-colors-primary-700, var(--ng-colors-text-brand-secondary-700));
311
+ font-family: monospace;
312
+ }
313
+
314
+ .fc-threshold-preview--empty {
315
+ background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));
316
+ border-color: var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
317
+ color: var(--nile-colors-text-placeholder, var(--ng-colors-text-placeholder));
318
+ font-family: inherit;
319
+ font-style: italic;
320
+ }
321
+
322
+ /* ── Preset ─── */
323
+ .fc-preset-list {
324
+ display: flex;
325
+ flex-direction: column;
326
+ gap: var(--nile-spacing-6px, var(--ng-spacing-1-5));
327
+ }
328
+
329
+ .fc-preset-item {
330
+ display: flex;
331
+ align-items: center;
332
+ gap: var(--nile-spacing-10px, var(--ng-spacing-2-5));
333
+ padding: var(--nile-spacing-10px, var(--ng-spacing-2-5)) var(--nile-spacing-14px, var(--ng-spacing-3-5));
334
+ border-radius: var(--nile-radius-md, var(--ng-radius-md));
335
+ border: 1.5px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
336
+ background: var(--nile-colors-white-base, var(--ng-color-base-white));
337
+ font-size: var(--nile-font-size-sm, 13px);
338
+ font-weight: var(--nile-font-weight-medium, var(--ng-font-weight-500));
339
+ color: var(--nile-colors-dark-500, var(--ng-colors-text-secondary-700));
340
+ cursor: pointer;
341
+ text-align: left;
342
+ transition: background 0.12s, border-color 0.12s;
343
+ }
344
+
345
+ .fc-preset-item:hover {
346
+ background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));
347
+ border-color: var(--nile-colors-neutral-500, var(--ng-colors-border-primary));
348
+ }
349
+
350
+ .fc-preset-item--selected {
351
+ border-color: var(--nile-colors-primary-600, var(--ng-colors-border-brand));
352
+ background: var(--nile-colors-primary-100, var(--ng-colors-bg-brand-primary));
353
+ color: var(--nile-colors-primary-700, var(--ng-colors-text-brand-secondary-700));
354
+ }
355
+
356
+ .fc-preset-icon {
357
+ font-size: var(--nile-type-scale-4, var(--ng-font-size-text-md));
358
+ line-height: 1;
359
+ }
360
+ `;
361
+ //# sourceMappingURL=nile-filter-chart.css.js.map
@@ -0,0 +1,96 @@
1
+ import { CSSResultArray, TemplateResult } from 'lit';
2
+ import NileElement from '../internal/nile-element.js';
3
+ import '@aquera/nile-elements/nile-select';
4
+ import '@aquera/nile-elements/nile-option';
5
+ import '@aquera/nile-elements/nile-input';
6
+ import '@aquera/nile-elements/nile-slide-toggle';
7
+ import '@aquera/nile-elements/nile-radio-group';
8
+ import '@aquera/nile-elements/nile-radio';
9
+ import '@aquera/nile-elements/nile-tag';
10
+ import '@aquera/nile-elements/nile-button-toggle-group';
11
+ import '@aquera/nile-elements/nile-button-toggle';
12
+ export type NileTagVariant = 'primary' | 'success' | 'normal' | 'warning' | 'error' | 'info';
13
+ export interface FilterOption {
14
+ label: string;
15
+ value: string;
16
+ /** Optional color dot (CSS color string). */
17
+ color?: string;
18
+ /** Optional nile-tag semantic variant — uses ng tokens directly (preferred over color). */
19
+ ngVariant?: NileTagVariant;
20
+ /** Optional icon name for preset variant. */
21
+ icon?: string;
22
+ }
23
+ export interface TreeNode {
24
+ label: string;
25
+ value: string;
26
+ expanded?: boolean;
27
+ children?: TreeNode[];
28
+ }
29
+ export interface FilterControl {
30
+ id: string;
31
+ label: string;
32
+ /** Optional subtitle shown below the label. */
33
+ description?: string;
34
+ selection: 'single' | 'multi';
35
+ variant: 'badge' | 'dropdown' | 'segmented' | 'radio' | 'toggle' | 'slider' | 'search' | 'comparison' | 'threshold' | 'tree' | 'preset';
36
+ options?: FilterOption[];
37
+ value?: string | string[] | number[] | boolean;
38
+ min?: number;
39
+ max?: number;
40
+ step?: number;
41
+ prefix?: string;
42
+ suffix?: string;
43
+ placeholder?: string;
44
+ valueB?: string;
45
+ operator?: string;
46
+ thresholdValue?: number | string;
47
+ treeData?: TreeNode[];
48
+ }
49
+ export interface FilterGroup {
50
+ type: 'group';
51
+ label: string;
52
+ description?: string;
53
+ collapsible?: boolean;
54
+ controls: FilterControl[];
55
+ }
56
+ export type FilterEntry = FilterControl | FilterGroup;
57
+ export type FilterChartConfig = {
58
+ type: 'filter';
59
+ controls: FilterEntry[];
60
+ [key: string]: unknown;
61
+ };
62
+ export type FilterChartSeparatedPayload = {
63
+ chart: FilterChartConfig;
64
+ aq?: Record<string, unknown>;
65
+ };
66
+ export declare class NileFilterChart extends NileElement {
67
+ static get styles(): CSSResultArray;
68
+ config: FilterChartSeparatedPayload | null;
69
+ private selectedValues;
70
+ private collapsedGroups;
71
+ connectedCallback(): void;
72
+ disconnectedCallback(): void;
73
+ updated(changed: Map<string, unknown>): void;
74
+ private _flatControls;
75
+ private _initValues;
76
+ private _set;
77
+ private _emitChange;
78
+ private _renderBadge;
79
+ private _renderDropdown;
80
+ private _renderSegmented;
81
+ private _renderRadio;
82
+ private _renderToggle;
83
+ private _renderSearch;
84
+ private _renderComparison;
85
+ private _renderThreshold;
86
+ private _renderPreset;
87
+ private _renderControl;
88
+ private _renderGroup;
89
+ render(): TemplateResult;
90
+ }
91
+ export default NileFilterChart;
92
+ declare global {
93
+ interface HTMLElementTagNameMap {
94
+ 'nile-filter-chart': NileFilterChart;
95
+ }
96
+ }
@@ -0,0 +1,453 @@
1
+ import { __decorate } from "tslib";
2
+ import { html, nothing } from 'lit';
3
+ import { customElement, property, state } from 'lit/decorators.js';
4
+ import { styles } from './nile-filter-chart.css.js';
5
+ import NileElement from '../internal/nile-element.js';
6
+ import '@aquera/nile-elements/nile-select';
7
+ import '@aquera/nile-elements/nile-option';
8
+ import '@aquera/nile-elements/nile-input';
9
+ import '@aquera/nile-elements/nile-slide-toggle';
10
+ // import '@aquera/nile-elements/nile-slider';
11
+ import '@aquera/nile-elements/nile-radio-group';
12
+ import '@aquera/nile-elements/nile-radio';
13
+ import '@aquera/nile-elements/nile-tag';
14
+ import '@aquera/nile-elements/nile-button-toggle-group';
15
+ import '@aquera/nile-elements/nile-button-toggle';
16
+ let NileFilterChart = class NileFilterChart extends NileElement {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.config = null;
20
+ this.selectedValues = new Map();
21
+ this.collapsedGroups = new Set();
22
+ }
23
+ static get styles() {
24
+ return [styles];
25
+ }
26
+ connectedCallback() {
27
+ super.connectedCallback();
28
+ this._initValues();
29
+ this.emit('nile-init');
30
+ }
31
+ disconnectedCallback() {
32
+ super.disconnectedCallback();
33
+ this.emit('nile-destroy');
34
+ }
35
+ updated(changed) {
36
+ super.updated(changed);
37
+ if (changed.has('config')) {
38
+ this._initValues();
39
+ }
40
+ }
41
+ _flatControls() {
42
+ const entries = this.config?.chart?.controls ?? [];
43
+ const out = [];
44
+ for (const entry of entries) {
45
+ if (entry.type === 'group') {
46
+ out.push(...entry.controls);
47
+ }
48
+ else {
49
+ out.push(entry);
50
+ }
51
+ }
52
+ return out;
53
+ }
54
+ _initValues() {
55
+ const controls = this._flatControls();
56
+ const map = new Map();
57
+ for (const ctrl of controls) {
58
+ if (ctrl.value !== undefined) {
59
+ map.set(ctrl.id, ctrl.value);
60
+ if (ctrl.variant === 'comparison') {
61
+ map.set(`${ctrl.id}__b`, ctrl.valueB ?? '');
62
+ }
63
+ if (ctrl.variant === 'threshold') {
64
+ map.set(`${ctrl.id}__op`, ctrl.operator ?? '>');
65
+ map.set(`${ctrl.id}__val`, ctrl.thresholdValue ?? '');
66
+ }
67
+ }
68
+ else {
69
+ if (ctrl.variant === 'slider') {
70
+ map.set(ctrl.id, [ctrl.min ?? 0, ctrl.max ?? 100]);
71
+ }
72
+ else if (ctrl.variant === 'toggle') {
73
+ const defaults = {};
74
+ (ctrl.options ?? []).forEach(o => { defaults[o.value] = false; });
75
+ map.set(ctrl.id, defaults);
76
+ }
77
+ else if (ctrl.selection === 'multi') {
78
+ map.set(ctrl.id, []);
79
+ }
80
+ else {
81
+ map.set(ctrl.id, '');
82
+ }
83
+ if (ctrl.variant === 'comparison')
84
+ map.set(`${ctrl.id}__b`, '');
85
+ if (ctrl.variant === 'threshold') {
86
+ map.set(`${ctrl.id}__op`, '>');
87
+ map.set(`${ctrl.id}__val`, '');
88
+ }
89
+ }
90
+ }
91
+ this.selectedValues = map;
92
+ }
93
+ _set(id, value) {
94
+ this.selectedValues = new Map(this.selectedValues).set(id, value);
95
+ this._emitChange();
96
+ }
97
+ _emitChange() {
98
+ const filters = {};
99
+ this.selectedValues.forEach((val, id) => { filters[id] = val; });
100
+ this.emit('nile-change', { filters });
101
+ }
102
+ // ── Badge ────────────────────────────────────────────────────────────────────
103
+ _renderBadge(ctrl) {
104
+ const current = this.selectedValues.get(ctrl.id);
105
+ return html `
106
+ <div class="fc-badge-group" role="group" aria-label="${ctrl.label}">
107
+ ${(ctrl.options ?? []).map(opt => {
108
+ const sel = ctrl.selection === 'multi'
109
+ ? (Array.isArray(current) && current.includes(opt.value))
110
+ : current === opt.value;
111
+ return html `
112
+ <nile-tag
113
+ pill
114
+ size="medium"
115
+ variant="${sel ? (opt.ngVariant ?? 'primary') : 'normal'}"
116
+ @click="${() => {
117
+ if (ctrl.selection === 'single') {
118
+ this._set(ctrl.id, sel ? '' : opt.value);
119
+ }
120
+ else {
121
+ const arr = Array.isArray(current) ? [...current] : [];
122
+ const idx = arr.indexOf(opt.value);
123
+ if (idx === -1)
124
+ arr.push(opt.value);
125
+ else
126
+ arr.splice(idx, 1);
127
+ this._set(ctrl.id, arr);
128
+ }
129
+ }}"
130
+ >${opt.label}</nile-tag>`;
131
+ })}
132
+ </div>`;
133
+ }
134
+ // ── Dropdown ─────────────────────────────────────────────────────────────────
135
+ _renderDropdown(ctrl) {
136
+ const raw = this.selectedValues.get(ctrl.id);
137
+ const current = Array.isArray(raw) ? raw : (raw ? [raw] : []);
138
+ const isMulti = ctrl.selection === 'multi';
139
+ return html `
140
+ <nile-select
141
+ .value="${isMulti ? current : (current[0] ?? '')}"
142
+ ?multiple="${isMulti}"
143
+ searchEnabled
144
+ placeholder="${ctrl.placeholder ?? `Select ${ctrl.label || 'option'}…`}"
145
+ @nile-change="${(e) => {
146
+ e.stopPropagation();
147
+ this._set(ctrl.id, e.detail.value);
148
+ }}"
149
+ >
150
+ ${(ctrl.options ?? []).map(opt => html `
151
+ <nile-option
152
+ value="${opt.value}"
153
+ ?selected="${isMulti ? current.includes(opt.value) : current[0] === opt.value}"
154
+ >${opt.label}</nile-option>`)}
155
+ </nile-select>`;
156
+ }
157
+ // ── Segmented ────────────────────────────────────────────────────────────────
158
+ _renderSegmented(ctrl) {
159
+ return html `
160
+ <div class="fc-segmented-scroll">
161
+ <nile-button-toggle-group
162
+ .value="${this.selectedValues.get(ctrl.id) ?? ''}"
163
+ ?multiple="${ctrl.selection === 'multi'}"
164
+ @nile-change="${(e) => { e.stopPropagation(); this._set(ctrl.id, e.detail.value); }}"
165
+ >
166
+ ${(ctrl.options ?? []).map(opt => html `
167
+ <nile-button-toggle value="${opt.value}">${opt.label}</nile-button-toggle>`)}
168
+ </nile-button-toggle-group>
169
+ </div>`;
170
+ }
171
+ // ── Radio ────────────────────────────────────────────────────────────────────
172
+ _renderRadio(ctrl) {
173
+ const current = this.selectedValues.get(ctrl.id) ?? '';
174
+ return html `
175
+ <nile-radio-group
176
+ .value="${current}"
177
+ @nile-change="${(e) => { e.stopPropagation(); this._set(ctrl.id, e.detail.value); }}"
178
+ >
179
+ ${(ctrl.options ?? []).map(opt => html `
180
+ <nile-radio value="${opt.value}" ?checked="${current === opt.value}">${opt.label}</nile-radio>`)}
181
+ </nile-radio-group>`;
182
+ }
183
+ // ── Toggle ───────────────────────────────────────────────────────────────────
184
+ _renderToggle(ctrl) {
185
+ const current = this.selectedValues.get(ctrl.id) ?? {};
186
+ return html `
187
+ <div class="fc-toggle-group">
188
+ ${(ctrl.options ?? []).map(opt => html `
189
+ <nile-slide-toggle
190
+ label="${opt.label}"
191
+ ?isChecked="${!!current[opt.value]}"
192
+ fullWidth
193
+ @nile-change="${(e) => {
194
+ e.stopPropagation();
195
+ const updated = { ...current, [opt.value]: e.detail.checked };
196
+ this._set(ctrl.id, updated);
197
+ }}"
198
+ ></nile-slide-toggle>`)}
199
+ </div>`;
200
+ }
201
+ // ── Slider ───────────────────────────────────────────────────────────────────
202
+ // private _renderSlider(ctrl: FilterControl): TemplateResult {
203
+ // const min = ctrl.min ?? 0;
204
+ // const max = ctrl.max ?? 100;
205
+ // const val: number[] = this.selectedValues.get(ctrl.id) ?? [min, max];
206
+ // const fmt = (n: number) => `${ctrl.prefix ?? ''}${n.toLocaleString()}${ctrl.suffix ?? ''}`;
207
+ //
208
+ // return html`
209
+ // <div class="fc-slider-wrap">
210
+ // <div class="fc-slider-label">
211
+ // <span class="fc-slider-range">${fmt(val[0])} — <strong>${fmt(val[1])}</strong></span>
212
+ // </div>
213
+ // <nile-slider
214
+ // .minValue="${min}"
215
+ // .maxValue="${max}"
216
+ // ?rangeSlider="${true}"
217
+ // .rangeOneValue="${val[0]}"
218
+ // .rangeTwoValue="${val[1]}"
219
+ // @nile-button-first-change-end="${(e: CustomEvent) => {
220
+ // e.stopPropagation();
221
+ // this._set(ctrl.id, [e.detail.value, val[1]]);
222
+ // }}"
223
+ // @nile-button-last-change-end="${(e: CustomEvent) => {
224
+ // e.stopPropagation();
225
+ // this._set(ctrl.id, [val[0], e.detail.value]);
226
+ // }}"
227
+ // >
228
+ // <span slot="prefix">${fmt(min)}</span>
229
+ // <span slot="suffix">${fmt(max)}</span>
230
+ // </nile-slider>
231
+ // </div>`;
232
+ // }
233
+ // ── Search ───────────────────────────────────────────────────────────────────
234
+ _renderSearch(ctrl) {
235
+ return html `
236
+ <nile-input
237
+ type="search"
238
+ placeholder="${ctrl.placeholder ?? `Search ${ctrl.label}…`}"
239
+ .value="${this.selectedValues.get(ctrl.id) ?? ''}"
240
+ clearable
241
+ @nile-input="${(e) => { e.stopPropagation(); this._set(ctrl.id, e.detail.value); }}"
242
+ ></nile-input>`;
243
+ }
244
+ // ── Comparison ───────────────────────────────────────────────────────────────
245
+ _renderComparison(ctrl) {
246
+ const valA = this.selectedValues.get(ctrl.id) ?? '';
247
+ const valB = this.selectedValues.get(`${ctrl.id}__b`) ?? '';
248
+ return html `
249
+ <div class="fc-comparison">
250
+ <nile-select
251
+ .value="${valA}"
252
+ placeholder="Period A"
253
+ @nile-change="${(e) => { e.stopPropagation(); this._set(ctrl.id, e.detail.value); }}"
254
+ >
255
+ ${(ctrl.options ?? []).map(o => html `<nile-option value="${o.value}">${o.label}</nile-option>`)}
256
+ </nile-select>
257
+ <span class="fc-vs">VS</span>
258
+ <nile-select
259
+ .value="${valB}"
260
+ placeholder="Period B"
261
+ @nile-change="${(e) => { e.stopPropagation(); this._set(`${ctrl.id}__b`, e.detail.value); }}"
262
+ >
263
+ ${(ctrl.options ?? []).map(o => html `<nile-option value="${o.value}">${o.label}</nile-option>`)}
264
+ </nile-select>
265
+ </div>`;
266
+ }
267
+ // ── Threshold ────────────────────────────────────────────────────────────────
268
+ _renderThreshold(ctrl) {
269
+ const metric = this.selectedValues.get(ctrl.id) ?? '';
270
+ const op = this.selectedValues.get(`${ctrl.id}__op`) ?? '>';
271
+ const val = this.selectedValues.get(`${ctrl.id}__val`) ?? '';
272
+ const operators = [
273
+ { value: '>', label: '> (greater)' },
274
+ { value: '>=', label: '>= (min)' },
275
+ { value: '<', label: '< (less)' },
276
+ { value: '<=', label: '<= (max)' },
277
+ { value: '=', label: '= (equals)' },
278
+ { value: '!=', label: '≠ (not)' },
279
+ ];
280
+ const metricLabel = (ctrl.options ?? []).find(o => o.value === metric)?.label ?? metric;
281
+ const hasPreview = metric && val !== '';
282
+ const previewText = hasPreview ? `${metricLabel} ${op} ${val}` : 'Set metric, condition, and value above';
283
+ return html `
284
+ <div class="fc-threshold">
285
+ <div class="fc-threshold-metric-row">
286
+ <span class="fc-threshold-where">WHERE</span>
287
+ <div class="fc-threshold-field fc-threshold-field--metric">
288
+ <span class="fc-threshold-field-label">Metric</span>
289
+ <nile-select
290
+ .value="${metric}"
291
+ placeholder="Select metric…"
292
+ @nile-change="${(e) => { e.stopPropagation(); this._set(ctrl.id, e.detail.value); }}"
293
+ >
294
+ ${(ctrl.options ?? []).map(o => html `<nile-option value="${o.value}">${o.label}</nile-option>`)}
295
+ </nile-select>
296
+ </div>
297
+ </div>
298
+ <div class="fc-threshold-cond-row">
299
+ <div class="fc-threshold-field fc-threshold-field--op">
300
+ <span class="fc-threshold-field-label">Condition</span>
301
+ <nile-select
302
+ .value="${op}"
303
+ @nile-change="${(e) => { e.stopPropagation(); this._set(`${ctrl.id}__op`, e.detail.value); }}"
304
+ >
305
+ ${operators.map(o => html `<nile-option value="${o.value}">${o.label}</nile-option>`)}
306
+ </nile-select>
307
+ </div>
308
+ <div class="fc-threshold-field fc-threshold-field--val">
309
+ <span class="fc-threshold-field-label">Value</span>
310
+ <nile-input
311
+ type="number"
312
+ placeholder="e.g. 10000"
313
+ .value="${String(val)}"
314
+ @nile-input="${(e) => { e.stopPropagation(); this._set(`${ctrl.id}__val`, e.detail.value); }}"
315
+ ></nile-input>
316
+ </div>
317
+ </div>
318
+ <div class="fc-threshold-preview ${hasPreview ? '' : 'fc-threshold-preview--empty'}">
319
+ ${hasPreview ? html `<strong>Filter:</strong>` : nothing}
320
+ ${previewText}
321
+ </div>
322
+ </div>`;
323
+ }
324
+ // ── Tree ─────────────────────────────────────────────────────────────────────
325
+ // private _renderTree(ctrl: FilterControl): TemplateResult {
326
+ // const renderNodes = (nodes: TreeNode[]): TemplateResult[] =>
327
+ // nodes.map(node => html`
328
+ // <nile-tree-item value="${node.value}" ?expanded="${node.expanded ?? false}">
329
+ // <span>${node.label}</span>
330
+ // ${node.children ? renderNodes(node.children) : nothing}
331
+ // </nile-tree-item>`);
332
+ //
333
+ // return html`
334
+ // <nile-tree
335
+ // selection="${ctrl.selection === 'multi' ? 'multiple' : 'single'}"
336
+ // @nile-selection-change="${(e: CustomEvent) => {
337
+ // e.stopPropagation();
338
+ // this._set(ctrl.id, e.detail?.detail?.selection ?? []);
339
+ // }}"
340
+ // >
341
+ // ${renderNodes(ctrl.treeData ?? [])}
342
+ // </nile-tree>`;
343
+ // }
344
+ // ── Preset ───────────────────────────────────────────────────────────────────
345
+ _renderPreset(ctrl) {
346
+ const current = this.selectedValues.get(ctrl.id);
347
+ return html `
348
+ <div class="fc-preset-list">
349
+ ${(ctrl.options ?? []).map(opt => html `
350
+ <button
351
+ class="fc-preset-item ${current === opt.value ? 'fc-preset-item--selected' : ''}"
352
+ @click="${() => this._set(ctrl.id, opt.value)}"
353
+ >
354
+ ${opt.icon ? html `<span class="fc-preset-icon">${opt.icon}</span>` : nothing}
355
+ ${opt.label}
356
+ </button>`)}
357
+ </div>`;
358
+ }
359
+ // ── Card wrapper ─────────────────────────────────────────────────────────────
360
+ _renderControl(ctrl) {
361
+ let body;
362
+ switch (ctrl.variant) {
363
+ case 'badge':
364
+ body = this._renderBadge(ctrl);
365
+ break;
366
+ case 'dropdown':
367
+ body = this._renderDropdown(ctrl);
368
+ break;
369
+ case 'segmented':
370
+ body = this._renderSegmented(ctrl);
371
+ break;
372
+ case 'radio':
373
+ body = this._renderRadio(ctrl);
374
+ break;
375
+ case 'toggle':
376
+ body = this._renderToggle(ctrl);
377
+ break;
378
+ // case 'slider': body = this._renderSlider(ctrl); break;
379
+ case 'search':
380
+ body = this._renderSearch(ctrl);
381
+ break;
382
+ case 'comparison':
383
+ body = this._renderComparison(ctrl);
384
+ break;
385
+ case 'threshold':
386
+ body = this._renderThreshold(ctrl);
387
+ break;
388
+ // case 'tree': body = this._renderTree(ctrl); break;
389
+ case 'preset':
390
+ body = this._renderPreset(ctrl);
391
+ break;
392
+ default: body = html ``;
393
+ }
394
+ return html `
395
+ <div class="fc-control" part="filter-control">
396
+ ${ctrl.label ? html `<div class="fc-control__label">${ctrl.label}</div>` : nothing}
397
+ ${ctrl.description ? html `<div class="fc-control__desc">${ctrl.description}</div>` : nothing}
398
+ <div class="fc-control__body">${body}</div>
399
+ </div>`;
400
+ }
401
+ _renderGroup(group) {
402
+ const collapsed = this.collapsedGroups.has(group.label);
403
+ const toggle = () => {
404
+ const next = new Set(this.collapsedGroups);
405
+ if (collapsed)
406
+ next.delete(group.label);
407
+ else
408
+ next.add(group.label);
409
+ this.collapsedGroups = next;
410
+ };
411
+ return html `
412
+ <div class="fc-group" part="filter-group">
413
+ <div class="fc-group__header ${group.collapsible ? 'fc-group__header--collapsible' : ''}"
414
+ @click="${group.collapsible ? toggle : nothing}">
415
+ <div class="fc-group__header-text">
416
+ <span class="fc-group__label">${group.label}</span>
417
+ ${group.description ? html `<span class="fc-group__desc">${group.description}</span>` : nothing}
418
+ </div>
419
+ ${group.collapsible ? html `
420
+ <span class="fc-group__chevron ${collapsed ? '' : 'fc-group__chevron--open'}">&#8250;</span>
421
+ ` : nothing}
422
+ </div>
423
+ ${collapsed ? nothing : html `
424
+ <div class="fc-group__body">
425
+ ${group.controls.map(ctrl => this._renderControl(ctrl))}
426
+ </div>`}
427
+ </div>`;
428
+ }
429
+ render() {
430
+ const entries = this.config?.chart?.controls ?? [];
431
+ return html `
432
+ <div class="fc-root" part="filter-root">
433
+ ${entries.map(entry => entry.type === 'group'
434
+ ? this._renderGroup(entry)
435
+ : this._renderControl(entry))}
436
+ </div>`;
437
+ }
438
+ };
439
+ __decorate([
440
+ property({ attribute: false })
441
+ ], NileFilterChart.prototype, "config", void 0);
442
+ __decorate([
443
+ state()
444
+ ], NileFilterChart.prototype, "selectedValues", void 0);
445
+ __decorate([
446
+ state()
447
+ ], NileFilterChart.prototype, "collapsedGroups", void 0);
448
+ NileFilterChart = __decorate([
449
+ customElement('nile-filter-chart')
450
+ ], NileFilterChart);
451
+ export { NileFilterChart };
452
+ export default NileFilterChart;
453
+ //# sourceMappingURL=nile-filter-chart.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aquera/nile-visualization",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "A visualization Library for the Nile Design System",
5
5
  "license": "MIT",
6
6
  "author": "Aquera Inc",