deepspotscreen-sdk 0.1.1 → 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,24 +1,24 @@
1
- class K {
1
+ class B {
2
2
  constructor(e, t) {
3
3
  this.tokenCache = /* @__PURE__ */ new Map(), this.baseUrl = e.replace(/\/$/, ""), this.apiKey = t;
4
4
  }
5
5
  // ── Embed Token ─────────────────────────────────────────────────────────────
6
6
  async getEmbedToken(e) {
7
- var r, a, o, l;
7
+ var o, i, a, f;
8
8
  const t = [
9
9
  e.dashboardId,
10
10
  e.embedType,
11
- (r = e.embedLevel) != null ? r : "",
12
- (a = e.componentId) != null ? a : "",
13
- (o = e.userId) != null ? o : ""
11
+ (o = e.embedLevel) != null ? o : "",
12
+ (i = e.componentId) != null ? i : "",
13
+ (a = e.userId) != null ? a : ""
14
14
  ].join(":"), s = this.tokenCache.get(t);
15
15
  if (s && s.expiresAt > Date.now() + 6e4) return s.token;
16
- const i = await this.post(
16
+ const r = await this.post(
17
17
  "/dashboard-builder/embed-token",
18
18
  {
19
19
  dashboardId: e.dashboardId,
20
20
  embedType: e.embedType,
21
- embedLevel: (l = e.embedLevel) != null ? l : "dashboard",
21
+ embedLevel: (f = e.embedLevel) != null ? f : "dashboard",
22
22
  componentId: e.componentId,
23
23
  userId: e.userId,
24
24
  tenantId: e.tenantId,
@@ -27,35 +27,40 @@ class K {
27
27
  { "x-deepspot-api-key": this.apiKey }
28
28
  );
29
29
  return this.tokenCache.set(t, {
30
- token: i.token,
31
- expiresAt: new Date(i.expiresAt).getTime()
32
- }), i.token;
30
+ token: r.token,
31
+ expiresAt: new Date(r.expiresAt).getTime()
32
+ }), r.token;
33
33
  }
34
34
  // ── Dashboard Render (lazy — loads one tab at a time) ──────────────────────
35
35
  async getDashboardRender(e, t, s = {}) {
36
- var r;
37
- const i = new URLSearchParams({
36
+ var o;
37
+ const r = new URLSearchParams({
38
38
  embedType: "dashboard",
39
- embedLevel: (r = s.embedLevel) != null ? r : "dashboard"
39
+ embedLevel: (o = s.embedLevel) != null ? o : "dashboard"
40
40
  });
41
- return s.pageId && i.set("pageId", s.pageId), s.tabId && i.set("tabId", s.tabId), s.filters && Object.entries(s.filters).forEach(([a, o]) => {
42
- o != null && o !== "" && i.set(`filter[${a}]`, Array.isArray(o) ? o.join(",") : String(o));
41
+ return s.pageId && r.set("pageId", s.pageId), s.tabId && r.set("tabId", s.tabId), s.filters && Object.entries(s.filters).forEach(([i, a]) => {
42
+ a != null && a !== "" && r.set(`filter[${i}]`, Array.isArray(a) ? a.join(",") : String(a));
43
+ }), s.filterOperators && Object.entries(s.filterOperators).forEach(([i, a]) => {
44
+ a && r.set(`filterOp[${i}]`, a);
45
+ }), s.localFilters && Object.entries(s.localFilters).forEach(([i, { value: a, operator: f }]) => {
46
+ const h = Array.isArray(a) ? a.join(",") : String(a);
47
+ h && (r.set(`localFilter[${i}]`, h), f && r.set(`localFilterOp[${i}]`, f));
43
48
  }), this.get(
44
- `/dashboard-builder/embed/${e}/render?${i}`,
49
+ `/dashboard-builder/embed/${e}/render?${r}`,
45
50
  { "x-embed-token": t }
46
51
  );
47
52
  }
48
53
  // ── Single Report Render ────────────────────────────────────────────────────
49
- async getReportRender(e, t, s, i = {}) {
50
- const r = new URLSearchParams({
54
+ async getReportRender(e, t, s, r = {}) {
55
+ const o = new URLSearchParams({
51
56
  embedType: "report",
52
57
  embedLevel: "report",
53
58
  componentId: t
54
59
  });
55
- return i.filters && Object.entries(i.filters).forEach(([a, o]) => {
56
- o != null && o !== "" && r.set(`filter[${a}]`, Array.isArray(o) ? o.join(",") : String(o));
60
+ return r.filters && Object.entries(r.filters).forEach(([i, a]) => {
61
+ a != null && a !== "" && o.set(`filter[${i}]`, Array.isArray(a) ? a.join(",") : String(a));
57
62
  }), this.get(
58
- `/dashboard-builder/embed/${e}/render?${r}`,
63
+ `/dashboard-builder/embed/${e}/render?${o}`,
59
64
  { "x-embed-token": s }
60
65
  );
61
66
  }
@@ -81,10 +86,10 @@ class K {
81
86
  * Called by TableRenderer whenever the user navigates to a different page.
82
87
  * Mirrors: GET /dashboard-builder/embed/:dashboardId/table/:componentId/data
83
88
  */
84
- async getTablePage(e, t, s, i, r) {
89
+ async getTablePage(e, t, s, r, o) {
85
90
  return this.get(
86
- `/dashboard-builder/embed/${e}/table/${t}/data?page=${s}&pageSize=${i}`,
87
- { "x-embed-token": r }
91
+ `/dashboard-builder/embed/${e}/table/${t}/data?page=${s}&pageSize=${r}`,
92
+ { "x-embed-token": o }
88
93
  );
89
94
  }
90
95
  // ── Filter Options ──────────────────────────────────────────────────────────
@@ -97,12 +102,12 @@ class K {
97
102
  */
98
103
  async getFilterOptions(e, t, s) {
99
104
  try {
100
- const i = await this.get(
105
+ const r = await this.get(
101
106
  `/dashboard-builder/embed/${e}/filters/${t}/options`,
102
107
  { "x-embed-token": s }
103
108
  );
104
- return Array.isArray(i) ? i : Array.isArray(i == null ? void 0 : i.options) ? i.options : Array.isArray(i == null ? void 0 : i.data) ? i.data : [];
105
- } catch (i) {
109
+ return Array.isArray(r) ? r : Array.isArray(r == null ? void 0 : r.options) ? r.options : Array.isArray(r == null ? void 0 : r.data) ? r.data : [];
110
+ } catch (r) {
106
111
  return [];
107
112
  }
108
113
  }
@@ -118,12 +123,12 @@ class K {
118
123
  return this.handleResponse(s);
119
124
  }
120
125
  async post(e, t, s = {}) {
121
- const i = await fetch(`${this.baseUrl}${e}`, {
126
+ const r = await fetch(`${this.baseUrl}${e}`, {
122
127
  method: "POST",
123
128
  headers: { "Content-Type": "application/json", ...s },
124
129
  body: JSON.stringify(t)
125
130
  });
126
- return this.handleResponse(i);
131
+ return this.handleResponse(r);
127
132
  }
128
133
  async delete(e, t = {}) {
129
134
  const s = await fetch(`${this.baseUrl}${e}`, {
@@ -136,9 +141,9 @@ class K {
136
141
  if (!e.ok) {
137
142
  let s = `HTTP ${e.status}`;
138
143
  try {
139
- const i = await e.json();
140
- s = (i == null ? void 0 : i.message) || (i == null ? void 0 : i.error) || s;
141
- } catch (i) {
144
+ const r = await e.json();
145
+ s = (r == null ? void 0 : r.message) || (r == null ? void 0 : r.error) || s;
146
+ } catch (r) {
142
147
  }
143
148
  throw new Error(`Deepspot SDK: ${s}`);
144
149
  }
@@ -146,7 +151,7 @@ class K {
146
151
  return (t == null ? void 0 : t.data) !== void 0 ? t.data : t;
147
152
  }
148
153
  }
149
- class E {
154
+ class z {
150
155
  constructor(e) {
151
156
  var t, s;
152
157
  this.destroyed = !1, this.refreshTimer = null, this.opts = e, this.filters = { ...e.activeFilters }, this.activePageId = (t = e.activePageId) != null ? t : "", this.activeTabId = (s = e.activeTabId) != null ? s : "";
@@ -203,19 +208,19 @@ class E {
203
208
  this.refreshTimer && clearTimeout(this.refreshTimer), this.refreshTimer = setTimeout(() => this.refresh(), 300);
204
209
  }
205
210
  async fetchData() {
206
- const { apiClient: e, dashboardId: t, componentId: s, token: i, embedType: r, embedLevel: a } = this.opts;
207
- return r === "report" && s ? e.getReportRender(t, s, i, {
211
+ const { apiClient: e, dashboardId: t, componentId: s, token: r, embedType: o, embedLevel: i } = this.opts;
212
+ return o === "report" && s ? e.getReportRender(t, s, r, {
208
213
  filters: this.filters
209
- }) : e.getDashboardRender(t, i, {
210
- embedLevel: a,
214
+ }) : e.getDashboardRender(t, r, {
215
+ embedLevel: i,
211
216
  pageId: this.activePageId || void 0,
212
217
  tabId: this.activeTabId || void 0,
213
218
  filters: this.filters
214
219
  });
215
220
  }
216
221
  }
217
- const be = 24, M = 10, O = 12;
218
- class ae {
222
+ const he = 24, D = 10, M = 12;
223
+ class ie {
219
224
  /**
220
225
  * @param containerWidth Actual pixel width of the grid container.
221
226
  * @param minY Minimum y value across all components (normalises
@@ -226,23 +231,23 @@ class ae {
226
231
  }
227
232
  /** Pixel width of one column unit */
228
233
  get colWidth() {
229
- return this.containerWidth / be;
234
+ return this.containerWidth / he;
230
235
  }
231
236
  /** Convert grid position {x,y,w,h} → CSS absolute pixel values */
232
237
  toPx(e) {
233
- const t = this.colWidth, s = O / 2;
238
+ const t = this.colWidth, s = M / 2;
234
239
  return {
235
240
  left: e.x * t + s,
236
- top: (e.y - this.minY) * M + s,
237
- width: e.w * t - O,
238
- height: e.h * M - O
241
+ top: (e.y - this.minY) * D + s,
242
+ width: e.w * t - M,
243
+ height: e.h * D - M
239
244
  };
240
245
  }
241
246
  /** Total pixel height required to fit all positioned components */
242
247
  static totalHeight(e) {
243
248
  if (!e.length) return 400;
244
- const t = Math.min(...e.map((i) => i.y));
245
- return (Math.max(...e.map((i) => i.y + i.h)) - t) * M + 40;
249
+ const t = Math.min(...e.map((r) => r.y));
250
+ return (Math.max(...e.map((r) => r.y + r.h)) - t) * D + 40;
246
251
  }
247
252
  /** Apply absolute positioning styles directly to a DOM element */
248
253
  applyStyles(e, t) {
@@ -250,21 +255,21 @@ class ae {
250
255
  e.style.position = "absolute", e.style.left = `${s.left}px`, e.style.top = `${s.top}px`, e.style.width = `${s.width}px`, e.style.height = `${s.height}px`;
251
256
  }
252
257
  }
253
- let N = null;
258
+ let H = null;
254
259
  function fe() {
255
- return N || (N = import("./apexcharts.common-k3hLWpB8.js").then((w) => w.a)), N;
260
+ return H || (H = import("./apexcharts.common-k3hLWpB8.js").then((A) => A.a)), H;
256
261
  }
257
262
  class ne {
258
263
  constructor(e) {
259
264
  this.chart = null, this.container = e;
260
265
  }
261
- async render(e, t, s, i) {
266
+ async render(e, t, s, r) {
262
267
  if (!t || t.length === 0) {
263
268
  this.renderEmpty(e.title);
264
269
  return;
265
270
  }
266
- const r = this.buildOptions(e, t, s, i);
267
- if (!r) {
271
+ const o = this.buildOptions(e, t, s, r);
272
+ if (!o) {
268
273
  this.renderEmpty(e.title);
269
274
  return;
270
275
  }
@@ -274,14 +279,14 @@ class ne {
274
279
  <div class="ds-chart-body" id="ds-chart-body-${e.id}"></div>
275
280
  </div>
276
281
  `;
277
- const a = this.container.querySelector(`#ds-chart-body-${e.id}`);
278
- if (a)
282
+ const i = this.container.querySelector(`#ds-chart-body-${e.id}`);
283
+ if (i)
279
284
  try {
280
285
  this.chart && this.chart.destroy();
281
- const { default: o } = await fe();
282
- this.chart = new o(a, r), this.chart.render();
283
- } catch (o) {
284
- console.error("[Deepspot SDK] ChartRenderer error:", o), a.innerHTML = '<div class="ds-chart-empty">Chart render error</div>';
286
+ const { default: a } = await fe();
287
+ this.chart = new a(i, o), this.chart.render();
288
+ } catch (a) {
289
+ console.error("[Deepspot SDK] ChartRenderer error:", a), i.innerHTML = '<div class="ds-chart-empty">Chart render error</div>';
285
290
  }
286
291
  }
287
292
  async update(e, t, s) {
@@ -289,14 +294,14 @@ class ne {
289
294
  await this.render(e, t, s);
290
295
  return;
291
296
  }
292
- const { series: i, categories: r } = this.extractSeriesAndCategories(e, t);
297
+ const { series: r, categories: o } = this.extractSeriesAndCategories(e, t);
293
298
  try {
294
299
  this.chart.updateOptions({
295
- series: i,
296
- xaxis: { categories: r },
300
+ series: r,
301
+ xaxis: { categories: o },
297
302
  theme: { mode: s }
298
303
  });
299
- } catch (a) {
304
+ } catch (i) {
300
305
  this.render(e, t, s);
301
306
  }
302
307
  }
@@ -309,13 +314,21 @@ class ne {
309
314
  this.chart = null;
310
315
  }
311
316
  // ── Private ─────────────────────────────────────────────────────────────────
312
- buildOptions(e, t, s, i) {
313
- const r = e.type;
314
- return r === "pie" || r === "donut" ? this.buildPieOptions(e, t, s, i) : r === "bar" || r === "line" || r === "area" ? this.buildCartesianOptions(e, t, s, i) : null;
317
+ buildOptions(e, t, s, r) {
318
+ const o = e.type;
319
+ return o === "pie" || o === "donut" ? this.buildPieOptions(e, t, s, r) : o === "bar" || o === "line" || o === "area" ? this.buildCartesianOptions(e, t, s, r) : null;
315
320
  }
316
- buildCartesianOptions(e, t, s, i) {
317
- var n;
318
- const { series: r, categories: a, xAxisLabel: o, yAxisLabel: l } = this.extractSeriesAndCategories(e, t), d = e.properties || {}, h = d.colors ? Object.values(d.colors) : ["#6366f1", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#3b82f6"], b = s === "dark";
321
+ buildCartesianOptions(e, t, s, r) {
322
+ var g;
323
+ const { series: o, categories: i, xAxisLabel: a, yAxisLabel: f, seriesColors: h } = this.extractSeriesAndCategories(e, t), v = e.properties || {}, m = ["#4f46e5", "#10b981", "#f59e0b", "#ef4444", "#3b82f6", "#8b5cf6", "#ec4899", "#14b8a6"];
324
+ let p;
325
+ if (h.length > 0 && h.length === o.length)
326
+ p = h;
327
+ else {
328
+ const u = v.colors ? Object.values(v.colors).filter(Boolean) : [];
329
+ p = u.length > 0 ? u : m;
330
+ }
331
+ const n = s === "dark";
319
332
  return {
320
333
  chart: {
321
334
  type: e.type === "area" ? "area" : e.type === "line" ? "line" : "bar",
@@ -325,35 +338,35 @@ class ne {
325
338
  fontFamily: "inherit",
326
339
  // Explicit height prevents ApexCharts from guessing the flex-child height
327
340
  // at render time (which can read as 0 before the browser settles layout).
328
- height: i != null ? i : "100%"
341
+ height: r != null ? r : "100%"
329
342
  },
330
343
  theme: { mode: s },
331
- series: r,
344
+ series: o,
332
345
  xaxis: {
333
- categories: a,
334
- title: { text: o },
346
+ categories: i,
347
+ title: { text: a },
335
348
  labels: {
336
- rotate: a.length > 8 ? -45 : 0,
337
- style: { colors: b ? "#94a3b8" : "#6b7280", fontSize: "11px" }
349
+ rotate: i.length > 8 ? -45 : 0,
350
+ style: { colors: n ? "#94a3b8" : "#6b7280", fontSize: "11px" }
338
351
  },
339
- axisBorder: { color: b ? "#334155" : "#e5e7eb" },
340
- axisTicks: { color: b ? "#334155" : "#e5e7eb" }
352
+ axisBorder: { color: n ? "#334155" : "#e5e7eb" },
353
+ axisTicks: { color: n ? "#334155" : "#e5e7eb" }
341
354
  },
342
355
  yaxis: {
343
- title: { text: l },
344
- labels: { style: { colors: b ? "#94a3b8" : "#6b7280", fontSize: "11px" } }
356
+ title: { text: f },
357
+ labels: { style: { colors: n ? "#94a3b8" : "#6b7280", fontSize: "11px" } }
345
358
  },
346
- colors: h,
359
+ colors: p,
347
360
  legend: {
348
- show: r.length > 1 || ((n = d.showLegend) != null ? n : !1),
361
+ show: o.length > 1 || ((g = v.showLegend) != null ? g : !1),
349
362
  position: "bottom",
350
- labels: { colors: b ? "#94a3b8" : "#6b7280" }
363
+ labels: { colors: n ? "#94a3b8" : "#6b7280" }
351
364
  },
352
365
  grid: {
353
- borderColor: b ? "#1e293b" : "#f3f4f6",
366
+ borderColor: n ? "#1e293b" : "#f3f4f6",
354
367
  strokeDashArray: 4,
355
368
  padding: {
356
- bottom: a.length > 8 ? 60 : 10
369
+ bottom: i.length > 8 ? 60 : 10
357
370
  }
358
371
  },
359
372
  tooltip: { theme: s },
@@ -366,34 +379,34 @@ class ne {
366
379
  plotOptions: {
367
380
  bar: {
368
381
  borderRadius: 4,
369
- distributed: r.length === 1
382
+ distributed: o.length === 1
370
383
  }
371
384
  }
372
385
  };
373
386
  }
374
- buildPieOptions(e, t, s, i) {
375
- const r = e.properties || {}, a = t.length > 0 ? Object.keys(t[0]) : [], o = a[0] || "label", l = a[1] || "value", d = t.map((p) => {
376
- var f;
377
- return String((f = p[o]) != null ? f : "");
378
- }), h = t.map((p) => Number(p[l]) || 0), b = s === "dark", n = r.colors ? Object.values(r.colors) : ["#6366f1", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#3b82f6"];
387
+ buildPieOptions(e, t, s, r) {
388
+ const o = e.properties || {}, i = t.length > 0 ? Object.keys(t[0]) : [], a = i[0] || "label", f = i[1] || "value", h = t.map((u) => {
389
+ var b;
390
+ return String((b = u[a]) != null ? b : "");
391
+ }), v = t.map((u) => Number(u[f]) || 0), m = s === "dark", p = ["#4f46e5", "#10b981", "#f59e0b", "#ef4444", "#3b82f6", "#8b5cf6", "#ec4899", "#14b8a6"], n = o.colors ? Object.values(o.colors).filter(Boolean) : [], g = n.length > 0 ? n : p;
379
392
  return {
380
393
  chart: {
381
394
  type: e.type,
382
395
  toolbar: { show: !1 },
383
396
  background: "transparent",
384
397
  fontFamily: "inherit",
385
- height: i != null ? i : "100%"
398
+ height: r != null ? r : "100%"
386
399
  },
387
400
  theme: { mode: s },
388
- series: h,
389
- labels: d,
390
- colors: n,
401
+ series: v,
402
+ labels: h,
403
+ colors: g,
391
404
  legend: {
392
405
  position: "bottom",
393
- labels: { colors: b ? "#94a3b8" : "#6b7280" }
406
+ labels: { colors: m ? "#94a3b8" : "#6b7280" }
394
407
  },
395
408
  tooltip: { theme: s },
396
- dataLabels: { enabled: d.length <= 8 },
409
+ dataLabels: { enabled: h.length <= 8 },
397
410
  plotOptions: {
398
411
  pie: {
399
412
  donut: { size: e.type === "donut" ? "65%" : "0%" }
@@ -402,38 +415,40 @@ class ne {
402
415
  };
403
416
  }
404
417
  extractSeriesAndCategories(e, t) {
405
- const s = e.properties || {}, i = t.length > 0 ? Object.keys(t[0]) : [], r = s.xAxis || i[0] || "x";
406
- let a = [];
418
+ const s = e.properties || {}, r = t.length > 0 ? Object.keys(t[0]) : [], o = s.xAxis || r[0] || "x";
419
+ let i = [];
407
420
  if (s.selectedYAxisColumn)
408
421
  try {
409
422
  const n = typeof s.selectedYAxisColumn == "string" ? JSON.parse(s.selectedYAxisColumn) : s.selectedYAxisColumn;
410
- Array.isArray(n) && (a = n.map(
411
- (p) => typeof p == "string" ? { column: p } : p
423
+ Array.isArray(n) && (i = n.map(
424
+ (g) => typeof g == "string" ? { column: g } : g
412
425
  ));
413
426
  } catch (n) {
414
427
  }
415
- if (!a.length && s.yAxis) {
428
+ if (!i.length && s.yAxis) {
416
429
  const n = s.yAxis;
417
- a = (Array.isArray(n) ? n : [n]).map((f) => ({ column: String(f) }));
430
+ i = (Array.isArray(n) ? n : [n]).map((u) => ({ column: String(u) }));
418
431
  }
419
- !a.length && i.length > 1 && (a = i.slice(1).map((n) => ({ column: n })));
420
- const o = t.map((n) => {
421
- var p;
422
- return String((p = n[r]) != null ? p : "");
423
- }), l = t.length > 0 ? Object.keys(t[0]) : [], d = a.map((n) => {
424
- const p = l.includes(n.column) ? n.column : null, f = p ? null : l.find((u) => u.includes(n.column) || n.column.includes(u)), m = !p && !f ? l.find((u) => {
425
- var g;
426
- return u !== r && !isNaN(Number((g = t[0]) == null ? void 0 : g[u]));
427
- }) : null, c = p || f || m || n.column;
432
+ !i.length && r.length > 1 && (i = r.slice(1).map((n) => ({ column: n })));
433
+ const a = t.map((n) => {
434
+ var g;
435
+ return String((g = n[o]) != null ? g : "");
436
+ }), f = t.length > 0 ? Object.keys(t[0]) : [], h = i.map((n) => {
437
+ const g = f.includes(n.column) ? n.column : null, u = g ? null : f.find((d) => d.includes(n.column) || n.column.includes(d)), b = !g && !u ? f.find((d) => {
438
+ var l;
439
+ return d !== o && !isNaN(Number((l = t[0]) == null ? void 0 : l[d]));
440
+ }) : null, c = g || u || b || n.column;
428
441
  return {
429
- name: n.label || n.column.replace(/_/g, " "),
430
- data: t.map((u) => {
431
- const g = u[c];
432
- return g != null ? Number(g) : null;
442
+ // Use the column name directly as the series label (matches builder behaviour).
443
+ // col.label often stores the chart title, not the series name — ignore it.
444
+ name: n.column,
445
+ data: t.map((d) => {
446
+ const l = d[c];
447
+ return l != null ? Number(l) : null;
433
448
  })
434
449
  };
435
- }), h = s.xAxisLabel || r.replace(/_/g, " "), b = s.yAxisLabel || (a.length === 1 ? a[0].column : "Values");
436
- return { series: d, categories: o, xAxisLabel: h, yAxisLabel: b };
450
+ }), v = i.map((n) => n.color || n.colorHex || "").filter(Boolean), m = s.xAxisLabel || o, p = s.yAxisLabel || (i.length === 1 ? i[0].column : "");
451
+ return { series: h, categories: a, xAxisLabel: m, yAxisLabel: p, seriesColors: v };
437
452
  }
438
453
  renderEmpty(e) {
439
454
  this.container.innerHTML = `
@@ -449,16 +464,16 @@ class le {
449
464
  this.columns = [], this.allData = [], this.currentRows = [], this.totalRows = 0, this.currentPage = 1, this.currentPageSize = 10, this.searchTerm = "", this.isLoading = !1;
450
465
  }
451
466
  // ── Public API ─────────────────────────────────────────────────────────────
452
- render(e, t, s, i) {
453
- var h;
454
- this.container = e, this.component = t, this.fetchPage = i, this.searchTerm = "", this.currentPage = 1, this.currentPageSize = 10;
455
- const r = s && !Array.isArray(s) && "rows" in s;
456
- if (r) {
457
- const b = s;
458
- this.currentRows = b.rows, this.allData = [], this.totalRows = b.total, this.currentPage = b.page, this.currentPageSize = b.pageSize;
467
+ render(e, t, s, r) {
468
+ var v;
469
+ this.container = e, this.component = t, this.fetchPage = r, this.searchTerm = "", this.currentPage = 1, this.currentPageSize = 10;
470
+ const o = s && !Array.isArray(s) && "rows" in s;
471
+ if (o) {
472
+ const m = s;
473
+ this.currentRows = m.rows, this.allData = [], this.totalRows = m.total, this.currentPage = m.page, this.currentPageSize = m.pageSize;
459
474
  } else
460
475
  this.allData = s || [], this.currentRows = [], this.totalRows = this.allData.length, this.currentPage = 1;
461
- if (r && s.rows.length === 0 && s.total === 0 || !r && this.allData.length === 0) {
476
+ if (o && s.rows.length === 0 && s.total === 0 || !o && this.allData.length === 0) {
462
477
  e.innerHTML = `
463
478
  <div class="ds-table-card">
464
479
  <div class="ds-table-header-row">
@@ -470,18 +485,18 @@ class le {
470
485
  `;
471
486
  return;
472
487
  }
473
- const a = r ? s.rows[0] : this.allData[0], o = a ? Object.keys(a) : [], l = ((h = t.properties) == null ? void 0 : h.columns) || [], d = l.length ? l.filter((b) => o.includes(b)) : [];
474
- this.columns = d.length ? d : o, this.paint();
488
+ const i = o ? s.rows[0] : this.allData[0], a = i ? Object.keys(i) : [], f = ((v = t.properties) == null ? void 0 : v.columns) || [], h = f.length ? f.filter((m) => a.includes(m)) : [];
489
+ this.columns = h.length ? h : a, this.paint();
475
490
  }
476
491
  // ── Rendering ──────────────────────────────────────────────────────────────
477
492
  paint() {
478
- const { rows: e, startNum: t } = this.getPageRows(), s = Math.max(1, Math.ceil(this.totalRows / this.currentPageSize)), i = (this.currentPage - 1) * this.currentPageSize + 1, r = this.fetchPage ? Math.min(i - 1 + (this.isLoading ? this.currentPageSize : e.length), this.totalRows) : Math.min(this.currentPage * this.currentPageSize, this.totalRows), a = this.columns.map((n) => `<th>${this.escape(this.formatHeader(n))}</th>`).join(""), o = e.length === 0 ? `<tr><td colspan="${this.columns.length + 1}" style="text-align:center;padding:20px;color:#9ca3af">No results found</td></tr>` : e.map((n, p) => {
479
- const f = this.columns.map((m) => {
480
- const c = n[m];
481
- return `<td title="${this.escape(String(c != null ? c : ""))}">${this.escape(this.formatValue(c))}</td>`;
493
+ const { rows: e, startNum: t } = this.getPageRows(), s = Math.max(1, Math.ceil(this.totalRows / this.currentPageSize)), r = (this.currentPage - 1) * this.currentPageSize + 1, o = this.fetchPage ? Math.min(r - 1 + (this.isLoading ? this.currentPageSize : e.length), this.totalRows) : Math.min(this.currentPage * this.currentPageSize, this.totalRows), i = this.columns.map((p) => `<th>${this.escape(this.formatHeader(p))}</th>`).join(""), a = e.length === 0 ? `<tr><td colspan="${this.columns.length + 1}" style="text-align:center;padding:20px;color:#9ca3af">No results found</td></tr>` : e.map((p, n) => {
494
+ const g = this.columns.map((u) => {
495
+ const b = p[u];
496
+ return `<td title="${this.escape(String(b != null ? b : ""))}">${this.escape(this.formatValue(b))}</td>`;
482
497
  }).join("");
483
- return `<tr><td class="ds-table-sno">${t + p}</td>${f}</tr>`;
484
- }).join(""), l = this.currentPageSize, d = [10, 25, 50, 100].map((n) => `<option value="${n}"${n === l ? " selected" : ""}>${n}</option>`).join(""), h = this.currentPage <= 1, b = this.currentPage >= s;
498
+ return `<tr><td class="ds-table-sno">${t + n}</td>${g}</tr>`;
499
+ }).join(""), f = this.currentPageSize, h = [10, 25, 50, 100].map((p) => `<option value="${p}"${p === f ? " selected" : ""}>${p}</option>`).join(""), v = this.currentPage <= 1, m = this.currentPage >= s;
485
500
  this.container.innerHTML = `
486
501
  <div class="ds-table-card">
487
502
  <div class="ds-table-header-row">
@@ -492,22 +507,22 @@ class le {
492
507
  </div>
493
508
  <div class="ds-table-scroll">
494
509
  <table class="ds-table">
495
- <thead><tr><th class="ds-table-sno-th">S.No</th>${a}</tr></thead>
496
- <tbody>${o}</tbody>
510
+ <thead><tr><th class="ds-table-sno-th">S.No</th>${i}</tr></thead>
511
+ <tbody>${a}</tbody>
497
512
  </table>
498
513
  </div>
499
514
  <div class="ds-table-footer">
500
515
  <div class="ds-table-info">
501
- <span>${i.toLocaleString()}–${r.toLocaleString()} of ${this.totalRows.toLocaleString()}</span>
516
+ <span>${r.toLocaleString()}–${o.toLocaleString()} of ${this.totalRows.toLocaleString()}</span>
502
517
  <span class="ds-table-rows-label">Rows:</span>
503
- <select class="ds-table-page-size">${d}</select>
518
+ <select class="ds-table-page-size">${h}</select>
504
519
  </div>
505
520
  <div class="ds-table-pagination">
506
- <button class="ds-table-pg-btn" data-action="first" ${h ? "disabled" : ""}>&#171;</button>
507
- <button class="ds-table-pg-btn" data-action="prev" ${h ? "disabled" : ""}>Previous</button>
521
+ <button class="ds-table-pg-btn" data-action="first" ${v ? "disabled" : ""}>&#171;</button>
522
+ <button class="ds-table-pg-btn" data-action="prev" ${v ? "disabled" : ""}>Previous</button>
508
523
  <span class="ds-table-pg-info">Page ${this.currentPage} of ${s}</span>
509
- <button class="ds-table-pg-btn" data-action="next" ${b ? "disabled" : ""}>Next</button>
510
- <button class="ds-table-pg-btn" data-action="last" ${b ? "disabled" : ""}>&#187;</button>
524
+ <button class="ds-table-pg-btn" data-action="next" ${m ? "disabled" : ""}>Next</button>
525
+ <button class="ds-table-pg-btn" data-action="last" ${m ? "disabled" : ""}>&#187;</button>
511
526
  </div>
512
527
  </div>
513
528
  ${this.isLoading ? '<div class="ds-table-loading-overlay"><div class="ds-embed-spinner"></div></div>' : ""}
@@ -525,9 +540,9 @@ class le {
525
540
  }), this.container.querySelectorAll("[data-action]").forEach((s) => {
526
541
  s.addEventListener("click", () => {
527
542
  if (s.disabled) return;
528
- const i = Math.max(1, Math.ceil(this.totalRows / this.currentPageSize)), r = s.dataset.action;
529
- let a = this.currentPage;
530
- r === "first" ? a = 1 : r === "prev" ? a = Math.max(1, this.currentPage - 1) : r === "next" ? a = Math.min(i, this.currentPage + 1) : r === "last" && (a = i), (a !== this.currentPage || r === "first" || r === "last") && (this.currentPage = a, this.fetchPage ? this.loadPage(a) : this.paint());
543
+ const r = Math.max(1, Math.ceil(this.totalRows / this.currentPageSize)), o = s.dataset.action;
544
+ let i = this.currentPage;
545
+ o === "first" ? i = 1 : o === "prev" ? i = Math.max(1, this.currentPage - 1) : o === "next" ? i = Math.min(r, this.currentPage + 1) : o === "last" && (i = r), (i !== this.currentPage || o === "first" || o === "last") && (this.currentPage = i, this.fetchPage ? this.loadPage(i) : this.paint());
531
546
  });
532
547
  });
533
548
  }
@@ -558,8 +573,8 @@ class le {
558
573
  const e = this.searchTerm.toLowerCase();
559
574
  return this.allData.filter(
560
575
  (t) => this.columns.some((s) => {
561
- var i;
562
- return String((i = t[s]) != null ? i : "").toLowerCase().includes(e);
576
+ var r;
577
+ return String((r = t[s]) != null ? r : "").toLowerCase().includes(e);
563
578
  })
564
579
  );
565
580
  }
@@ -576,24 +591,35 @@ class le {
576
591
  }
577
592
  class ce {
578
593
  render(e, t, s) {
579
- const i = t.properties || {};
580
- let r = null;
594
+ var g;
595
+ const r = t.properties || {};
596
+ let o = null;
581
597
  if (s && s.length > 0) {
582
- const f = s[0], m = i.metric || Object.keys(f)[0], c = f[m];
598
+ const u = s[0], b = r.metric || Object.keys(u)[0], c = u[b];
583
599
  if (c !== void 0)
584
- r = c;
600
+ o = c;
585
601
  else {
586
- const u = Object.keys(f).find(
587
- (g) => g.includes(m) || m.includes(g)
602
+ const d = Object.keys(u).find(
603
+ (l) => l.includes(b) || b.includes(l)
588
604
  );
589
- r = f[u != null ? u : Object.keys(f)[0]];
605
+ o = u[d != null ? d : Object.keys(u)[0]];
590
606
  }
591
607
  }
592
- const a = this.formatValue(r, i), o = i.styleConfig || {}, l = o.backgroundColor || i.backgroundColor || "", d = o.color || o.textColor || i.textColor || "", h = t.title || "", b = l ? `background:${l};border-color:${l};` : "", n = d ? `color:${d};` : "", p = d ? `color:${d};opacity:0.7;` : "";
608
+ let i;
609
+ if (o == null)
610
+ try {
611
+ const u = (g = r == null ? void 0 : r.data) == null ? void 0 : g.dataContent, b = u ? typeof u == "string" ? JSON.parse(u) : u : null;
612
+ i = b != null && b.value ? String(b.value) : "—";
613
+ } catch (u) {
614
+ i = "—";
615
+ }
616
+ else
617
+ i = this.formatValue(o, r);
618
+ const a = r.styleConfig || {}, f = a.backgroundColor || r.backgroundColor || "", h = a.color || a.textColor || r.textColor || "", v = t.title || "", m = f ? `background:${f};border:none;` : "", p = h ? `color:${h};` : "", n = h ? `color:${h};opacity:0.7;` : "";
593
619
  e.innerHTML = `
594
- <div class="ds-card"${b ? ` style="${b}"` : ""}>
595
- <div class="ds-card-label"${p ? ` style="${p}"` : ""}>${h}</div>
596
- <div class="ds-card-value"${n ? ` style="${n}"` : ""}>${a}</div>
620
+ <div class="ds-card"${m ? ` style="${m}"` : ""}>
621
+ <div class="ds-card-label"${n ? ` style="${n}"` : ""}>${v}</div>
622
+ <div class="ds-card-value"${p ? ` style="${p}"` : ""}>${i}</div>
597
623
  </div>
598
624
  `;
599
625
  }
@@ -601,126 +627,394 @@ class ce {
601
627
  if (e == null) return "—";
602
628
  const s = Number(e);
603
629
  if (isNaN(s)) return String(e);
604
- const i = (t == null ? void 0 : t.prefix) || (t == null ? void 0 : t.currencySymbol) || "", r = (t == null ? void 0 : t.suffix) || "";
605
- let a;
606
- return Math.abs(s) >= 1e6 ? a = (s / 1e6).toFixed(1) + "M" : Math.abs(s) >= 1e3 ? a = (s / 1e3).toFixed(1) + "K" : a = s % 1 === 0 ? s.toLocaleString() : s.toFixed(2), `${i}${a}${r}`;
630
+ const r = (t == null ? void 0 : t.prefix) || (t == null ? void 0 : t.currencySymbol) || "", o = (t == null ? void 0 : t.suffix) || "", i = s % 1 === 0 ? s.toLocaleString() : parseFloat(s.toFixed(2)).toLocaleString();
631
+ return `${r}${i}${o}`;
607
632
  }
608
633
  }
609
- class pe {
634
+ const be = [
635
+ { value: "=", label: "Equals (=)" },
636
+ { value: "!=", label: "Not Equals (!=)" },
637
+ { value: "LIKE", label: "Contains" },
638
+ { value: "NOT LIKE", label: "Not Contains" },
639
+ { value: "STARTS_WITH", label: "Starts With" },
640
+ { value: "IN", label: "In (Multiple)" },
641
+ { value: "NOT IN", label: "Not In" },
642
+ { value: ">", label: "Greater Than (>)" },
643
+ { value: ">=", label: "Greater or Equal (>=)" },
644
+ { value: "<", label: "Less Than (<)" },
645
+ { value: "<=", label: "Less or Equal (<=)" },
646
+ { value: "IS NULL", label: "Is Empty" },
647
+ { value: "IS NOT NULL", label: "Is Not Empty" }
648
+ ];
649
+ class ge {
610
650
  constructor(e, t) {
611
- this.currentValues = {}, this.container = e, this.onFilterChange = t;
651
+ this.filters = [], this.state = /* @__PURE__ */ new Map(), this.activePopoverId = null, this.container = e, this.onFilterChange = t;
612
652
  }
653
+ // ── Public API ─────────────────────────────────────────────────────────────
613
654
  render(e, t = {}) {
655
+ var s, r, o;
614
656
  if (!e || e.length === 0) {
615
657
  this.container.style.display = "none";
616
658
  return;
617
659
  }
618
- this.container.style.display = "", this.currentValues = { ...t }, this.container.innerHTML = `<div class="ds-filter-bar">${e.map((s) => this.renderFilter(s, t[s.applyToField || s.filterId])).join("")}</div>`, this.attachListeners(e);
660
+ this.container.style.display = "", this.filters = e, this.state.clear();
661
+ for (const i of e) {
662
+ const a = i.applyToField || i.filterId, f = (s = t[i.filterId]) != null ? s : t[a], h = f ? Array.isArray(f) ? f.map(String) : [String(f)] : [];
663
+ this.state.set(i.filterId, {
664
+ operator: i.filterOperator || "=",
665
+ tempValues: [...h],
666
+ activeValues: [...h],
667
+ options: (o = (r = i.options) == null ? void 0 : r.map((v) => v)) != null ? o : [],
668
+ loading: !1,
669
+ search: ""
670
+ });
671
+ }
672
+ this.paint();
619
673
  }
620
- /**
621
- * Populate a dropdown / multi-select with options fetched from the API.
622
- * Called asynchronously after the filter bar is rendered.
623
- */
624
674
  updateOptions(e, t) {
625
- const s = this.container.querySelector(
626
- `select[data-filter-id="${e}"]`
627
- );
628
- if (!s) return;
629
- const i = s.value;
630
- s.innerHTML = '<option value="">All</option>' + t.map(
631
- (r) => `<option value="${this.escAttr(r)}">${this.escText(r)}</option>`
632
- ).join(""), i && t.includes(i) && (s.value = i);
675
+ const s = this.state.get(e);
676
+ s && (s.loading = !1, s.options = t, this.refreshPopoverOpts(e));
677
+ }
678
+ setLoading(e, t) {
679
+ const s = this.state.get(e);
680
+ s && (s.loading = t, this.refreshPopoverOpts(e));
633
681
  }
634
- /** Update the stored values without re-rendering (for programmatic setFilter) */
635
682
  updateValues(e) {
636
- Object.assign(this.currentValues, e), Object.entries(e).forEach(([t, s]) => {
637
- const i = this.container.querySelector(`[data-filter-id="${t}"]`);
638
- i && s !== void 0 && (i.value = String(s));
639
- });
683
+ for (const [t, s] of Object.entries(e)) {
684
+ const r = this.filters.find(
685
+ (a) => (a.applyToField || a.filterId) === t || a.filterId === t
686
+ );
687
+ if (!r) continue;
688
+ const o = this.state.get(r.filterId);
689
+ if (!o) continue;
690
+ const i = s ? Array.isArray(s) ? s.map(String) : [String(s)] : [];
691
+ o.activeValues = i, o.tempValues = [...i];
692
+ }
693
+ this.repaintBadges();
640
694
  }
641
- // ── Private ─────────────────────────────────────────────────────────────────
642
- renderFilter(e, t) {
643
- const s = `ds-filter-${e.filterId}`, i = `<label class="ds-filter-label" for="${s}">${e.label}</label>`;
644
- switch (e.type) {
645
- case "dropdown":
646
- return `
647
- <div class="ds-filter-item">
648
- ${i}
649
- <select class="ds-filter-select" id="${s}" data-filter-id="${e.filterId}">
650
- <option value="">All</option>
651
- ${(e.options || []).map((o) => `
652
- <option value="${this.escAttr(o)}" ${t === o ? "selected" : ""}>
653
- ${this.escText(o)}
654
- </option>
655
- `).join("")}
656
- </select>
657
- </div>`;
658
- case "multi-select":
659
- return `
660
- <div class="ds-filter-item">
661
- ${i}
662
- <select class="ds-filter-select" id="${s}" data-filter-id="${e.filterId}" multiple size="1">
663
- ${(e.options || []).map((o) => `
664
- <option value="${this.escAttr(o)}">${this.escText(o)}</option>
665
- `).join("")}
695
+ // ── Paint badge bar ─────────────────────────────────────────────────────────
696
+ paint() {
697
+ this.container.innerHTML = `
698
+ <div class="ds-fp-v3">
699
+ <div class="ds-fp-v3-bar" id="ds-fp-v3-bar">
700
+ <span class="ds-fp-v3-label">
701
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
702
+ stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
703
+ <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
704
+ </svg>
705
+ Filters:
706
+ </span>
707
+ <div class="ds-fp-v3-badges" id="ds-fp-v3-badges">
708
+ ${this.filters.map((e) => this.renderBadge(e)).join("")}
709
+ </div>
710
+ </div>
711
+ <!-- Popovers rendered per filter -->
712
+ ${this.filters.map((e) => this.renderPopoverHtml(e)).join("")}
713
+ </div>`, this.bindAll(), this.closePopoverOnOutsideClick();
714
+ }
715
+ renderBadge(e) {
716
+ return `
717
+ <button type="button"
718
+ class="ds-fp-v3-badge${this.state.get(e.filterId).activeValues.length > 0 ? " ds-fp-v3-badge-active" : ""}"
719
+ data-filter-id="${this.escAttr(e.filterId)}">
720
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor"
721
+ stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
722
+ <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
723
+ </svg>
724
+ ${this.escText(e.label)}
725
+ </button>`;
726
+ }
727
+ renderPopoverHtml(e) {
728
+ const t = this.state.get(e.filterId);
729
+ return `
730
+ <div class="ds-fp-v3-pop" id="ds-fp-pop-${this.escAttr(e.filterId)}" style="display:none">
731
+ <div class="ds-fp-v3-pop-hd">
732
+ <span class="ds-fp-v3-pop-title">
733
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
734
+ stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
735
+ <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
736
+ </svg>
737
+ Filter
738
+ </span>
739
+ <div class="ds-fp-v3-pop-actions">
740
+ <button type="button" class="ds-fp-v3-pop-icon-btn ds-fp-v3-pop-close-btn"
741
+ data-filter-id="${this.escAttr(e.filterId)}" title="Close">
742
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
743
+ stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
744
+ <line x1="18" y1="6" x2="6" y2="18"/>
745
+ <line x1="6" y1="6" x2="18" y2="18"/>
746
+ </svg>
747
+ </button>
748
+ </div>
749
+ </div>
750
+ <div class="ds-fp-v3-pop-body">
751
+ <div class="ds-fp-v3-field">
752
+ <label class="ds-fp-v3-field-lbl">CONDITION</label>
753
+ <select class="ds-fp-v3-condition" data-filter-id="${this.escAttr(e.filterId)}">
754
+ ${be.map(
755
+ (s) => `<option value="${this.escAttr(s.value)}"${s.value === t.operator ? " selected" : ""}>${s.label}</option>`
756
+ ).join("")}
666
757
  </select>
667
- </div>`;
668
- case "date-range":
669
- const r = (t == null ? void 0 : t.from) || "", a = (t == null ? void 0 : t.to) || "";
670
- return `
671
- <div class="ds-filter-item">
672
- ${i}
673
- <div class="ds-date-range-inputs">
674
- <input type="date" class="ds-filter-input"
675
- data-filter-id="${e.filterId}" data-date-part="from"
676
- value="${r}" />
677
- <span>–</span>
678
- <input type="date" class="ds-filter-input"
679
- data-filter-id="${e.filterId}" data-date-part="to"
680
- value="${a}" />
681
- </div>
682
- </div>`;
683
- case "text-input":
684
- return `
685
- <div class="ds-filter-item">
686
- ${i}
687
- <input type="text" class="ds-filter-input" id="${s}"
688
- data-filter-id="${e.filterId}"
689
- value="${this.escAttr(String(t != null ? t : ""))}"
690
- placeholder="Search..." />
691
- </div>`;
692
- case "number-input":
693
- return `
694
- <div class="ds-filter-item">
695
- ${i}
696
- <input type="number" class="ds-filter-input" id="${s}"
697
- data-filter-id="${e.filterId}"
698
- value="${this.escAttr(String(t != null ? t : ""))}" />
699
- </div>`;
700
- default:
701
- return "";
702
- }
758
+ </div>
759
+ <div class="ds-fp-v3-field">
760
+ <label class="ds-fp-v3-field-lbl">VALUES</label>
761
+ ${this.renderValuesControl(e, t)}
762
+ </div>
763
+ </div>
764
+ <div class="ds-fp-v3-pop-footer">
765
+ <button type="button" class="ds-fp-v3-clear-btn"
766
+ data-filter-id="${this.escAttr(e.filterId)}">Clear</button>
767
+ <button type="button" class="ds-fp-v3-apply-btn"
768
+ data-filter-id="${this.escAttr(e.filterId)}">Apply Filter</button>
769
+ </div>
770
+ </div>`;
703
771
  }
704
- attachListeners(e) {
705
- const t = {};
706
- this.container.querySelectorAll("[data-filter-id]").forEach((s) => {
707
- const i = s.dataset.filterId, r = s.dataset.datePart, a = e.find((l) => l.filterId === i), o = () => {
708
- let l;
709
- if ((a == null ? void 0 : a.type) === "date-range" && r)
710
- t[i] = t[i] || {}, t[i][r] = s.value, l = { ...t[i] };
711
- else if ((a == null ? void 0 : a.type) === "multi-select") {
712
- const d = s;
713
- l = Array.from(d.selectedOptions).map((h) => h.value).filter(Boolean);
714
- } else
715
- l = s.value;
716
- this.currentValues[i] = l, this.onFilterChange(i, l);
717
- };
718
- s.addEventListener(
719
- (a == null ? void 0 : a.type) === "text-input" ? "input" : "change",
720
- o
772
+ renderValuesControl(e, t) {
773
+ var s, r, o;
774
+ return e.type === "date-range" ? `
775
+ <div class="ds-fp-v3-date-range">
776
+ <input type="date" class="ds-fp-v3-date" data-part="from"
777
+ data-filter-id="${this.escAttr(e.filterId)}"
778
+ value="${this.escAttr((s = t.tempValues[0]) != null ? s : "")}" placeholder="From"/>
779
+ <span class="ds-fp-v3-date-sep">–</span>
780
+ <input type="date" class="ds-fp-v3-date" data-part="to"
781
+ data-filter-id="${this.escAttr(e.filterId)}"
782
+ value="${this.escAttr((r = t.tempValues[1]) != null ? r : "")}" placeholder="To"/>
783
+ </div>` : e.type === "text-input" || e.type === "number-input" ? `<input type="${e.type === "number-input" ? "number" : "text"}"
784
+ class="ds-fp-v3-text-input"
785
+ data-filter-id="${this.escAttr(e.filterId)}"
786
+ value="${this.escAttr((o = t.tempValues[0]) != null ? o : "")}"
787
+ placeholder="Enter value…"/>` : this.renderMultiSelectControl(e, t);
788
+ }
789
+ renderMultiSelectControl(e, t) {
790
+ const s = t.tempValues.length === 0 ? "" : t.tempValues.length === 1 ? this.optLabel(e.filterId, t.tempValues[0]) : `${t.tempValues.length} selected`;
791
+ return `
792
+ <div class="ds-fp-v3-ms-wrap" data-filter-id="${this.escAttr(e.filterId)}">
793
+ <div class="ds-fp-v3-ms-trigger" data-filter-id="${this.escAttr(e.filterId)}">
794
+ <span class="ds-fp-v3-ms-display">${s || "Type to search..."}</span>
795
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
796
+ stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
797
+ <polyline points="6 9 12 15 18 9"/>
798
+ </svg>
799
+ </div>
800
+ <div class="ds-fp-v3-ms-drop" id="ds-fp-drop-${this.escAttr(e.filterId)}" style="display:none">
801
+ <input type="text" class="ds-fp-v3-ms-search"
802
+ data-filter-id="${this.escAttr(e.filterId)}"
803
+ placeholder="Search ${this.escAttr(e.label)}…"
804
+ value="${this.escAttr(t.search)}"/>
805
+ <div class="ds-fp-v3-ms-opts" id="ds-fp-opts-${this.escAttr(e.filterId)}">
806
+ ${this.renderOpts(e.filterId, t)}
807
+ </div>
808
+ </div>
809
+ ${t.tempValues.length > 0 ? `
810
+ <div class="ds-fp-v3-chips" id="ds-fp-chips-${this.escAttr(e.filterId)}">
811
+ ${t.tempValues.map((r) => `
812
+ <span class="ds-fp-v3-chip">
813
+ ${this.escText(this.optLabel(e.filterId, r))}
814
+ <button type="button" class="ds-fp-v3-chip-x"
815
+ data-val="${this.escAttr(r)}"
816
+ data-filter-id="${this.escAttr(e.filterId)}">✕</button>
817
+ </span>`).join("")}
818
+ </div>` : `<div class="ds-fp-v3-chips" id="ds-fp-chips-${this.escAttr(e.filterId)}"></div>`}
819
+ </div>`;
820
+ }
821
+ renderOpts(e, t) {
822
+ if (t.loading) return '<div class="ds-fp-v3-opts-msg"><span class="ds-dd-spinner"></span> Loading…</div>';
823
+ const s = t.search.toLowerCase(), r = t.options.filter(
824
+ (o) => (typeof o == "string" ? o : o.label).toLowerCase().includes(s)
825
+ );
826
+ return t.options.length ? r.length ? r.map((o) => {
827
+ const i = typeof o == "string" ? o : o.value, a = typeof o == "string" ? o : o.label, f = t.tempValues.includes(i);
828
+ return `
829
+ <label class="ds-fp-v3-opt${f ? " ds-fp-v3-opt-on" : ""}" data-val="${this.escAttr(i)}"
830
+ data-filter-id="${this.escAttr(e)}">
831
+ <span class="ds-fp-v3-cb${f ? " ds-fp-v3-cb-on" : ""}">
832
+ ${f ? `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"
833
+ stroke-linecap="round" stroke-linejoin="round" width="10" height="10">
834
+ <polyline points="20 6 9 17 4 12"/>
835
+ </svg>` : ""}
836
+ </span>
837
+ <span>${this.escText(a)}</span>
838
+ </label>`;
839
+ }).join("") : '<div class="ds-fp-v3-opts-msg">No matches</div>' : '<div class="ds-fp-v3-opts-msg">No options yet</div>';
840
+ }
841
+ // ── Event binding ──────────────────────────────────────────────────────────
842
+ bindAll() {
843
+ var e;
844
+ this.container.querySelectorAll(".ds-fp-v3-badge").forEach((t) => {
845
+ t.addEventListener("click", (s) => {
846
+ s.stopPropagation();
847
+ const r = t.dataset.filterId;
848
+ this.togglePopover(r, t);
849
+ });
850
+ }), (e = this.container.querySelector("#ds-fp-v3-add")) == null || e.addEventListener("click", (t) => {
851
+ t.stopPropagation();
852
+ const s = this.filters.find((o) => this.state.get(o.filterId).activeValues.length === 0);
853
+ if (!s) return;
854
+ const r = this.container.querySelector(
855
+ `.ds-fp-v3-badge[data-filter-id="${s.filterId}"]`
721
856
  );
857
+ r && this.togglePopover(s.filterId, r);
858
+ }), this.filters.forEach((t) => this.bindPopover(t));
859
+ }
860
+ bindPopover(e) {
861
+ var o, i, a, f, h, v, m;
862
+ const t = this.container.querySelector(`#ds-fp-pop-${e.filterId}`);
863
+ if (!t) return;
864
+ const s = this.state.get(e.filterId);
865
+ (o = t.querySelector(`.ds-fp-v3-condition[data-filter-id="${e.filterId}"]`)) == null || o.addEventListener("change", (p) => {
866
+ s.operator = p.target.value;
867
+ }), (i = t.querySelector(`.ds-fp-v3-text-input[data-filter-id="${e.filterId}"]`)) == null || i.addEventListener("input", (p) => {
868
+ const n = p.target.value;
869
+ s.tempValues = n ? [n] : [];
870
+ }), t.querySelectorAll(`.ds-fp-v3-date[data-filter-id="${e.filterId}"]`).forEach((p) => {
871
+ p.addEventListener("change", () => {
872
+ var u, b, c, d;
873
+ const n = (b = (u = t.querySelector(`[data-part="from"][data-filter-id="${e.filterId}"]`)) == null ? void 0 : u.value) != null ? b : "", g = (d = (c = t.querySelector(`[data-part="to"][data-filter-id="${e.filterId}"]`)) == null ? void 0 : c.value) != null ? d : "";
874
+ s.tempValues = [n, g];
875
+ });
876
+ }), (a = t.querySelector(`.ds-fp-v3-ms-trigger[data-filter-id="${e.filterId}"]`)) == null || a.addEventListener("click", (p) => {
877
+ p.stopPropagation();
878
+ const n = t.querySelector(`#ds-fp-drop-${e.filterId}`);
879
+ if (n) {
880
+ const g = n.style.display !== "none";
881
+ if (n.style.display = g ? "none" : "", !g) {
882
+ const u = n.querySelector(".ds-fp-v3-ms-search");
883
+ u == null || u.focus();
884
+ }
885
+ }
886
+ });
887
+ const r = t.querySelector(`.ds-fp-v3-ms-search[data-filter-id="${e.filterId}"]`);
888
+ r && r.addEventListener("input", () => {
889
+ s.search = r.value, this.repaintOpts(e.filterId);
890
+ }), this.bindOpts(e.filterId, t), this.bindChips(e.filterId, t), (f = t.querySelector(`.ds-fp-v3-pop-clear-btn[data-filter-id="${e.filterId}"]`)) == null || f.addEventListener("click", () => this.clearFilter(e.filterId)), (h = t.querySelector(`.ds-fp-v3-pop-close-btn[data-filter-id="${e.filterId}"]`)) == null || h.addEventListener("click", () => this.closePopover(e.filterId)), (v = t.querySelector(`.ds-fp-v3-clear-btn[data-filter-id="${e.filterId}"]`)) == null || v.addEventListener("click", () => this.clearFilter(e.filterId)), (m = t.querySelector(`.ds-fp-v3-apply-btn[data-filter-id="${e.filterId}"]`)) == null || m.addEventListener("click", () => this.applyFilter(e.filterId));
891
+ }
892
+ bindOpts(e, t) {
893
+ const s = this.state.get(e);
894
+ t.querySelectorAll(`.ds-fp-v3-opt[data-filter-id="${e}"]`).forEach((r) => {
895
+ r.addEventListener("click", (o) => {
896
+ var a;
897
+ o.stopPropagation();
898
+ const i = (a = r.dataset.val) != null ? a : "";
899
+ s.tempValues.includes(i) ? s.tempValues = s.tempValues.filter((f) => f !== i) : s.tempValues = [...s.tempValues, i], this.repaintOpts(e), this.repaintChips(e);
900
+ });
901
+ });
902
+ }
903
+ bindChips(e, t) {
904
+ const s = this.state.get(e);
905
+ t.querySelectorAll(`.ds-fp-v3-chip-x[data-filter-id="${e}"]`).forEach((r) => {
906
+ r.addEventListener("click", (o) => {
907
+ o.stopPropagation(), s.tempValues = s.tempValues.filter((i) => {
908
+ var a;
909
+ return i !== ((a = r.dataset.val) != null ? a : "");
910
+ }), this.repaintOpts(e), this.repaintChips(e);
911
+ });
722
912
  });
723
913
  }
914
+ // ── Popover show/hide ──────────────────────────────────────────────────────
915
+ togglePopover(e, t) {
916
+ if (this.activePopoverId === e) {
917
+ this.closePopover(e);
918
+ return;
919
+ }
920
+ this.activePopoverId && this.closePopover(this.activePopoverId);
921
+ const s = this.container.querySelector(`#ds-fp-pop-${e}`);
922
+ if (!s) return;
923
+ const r = this.container.getBoundingClientRect(), o = t.getBoundingClientRect();
924
+ s.style.top = `${o.bottom - r.top + 4}px`, s.style.left = `${o.left - r.left}px`, s.style.display = "", this.activePopoverId = e;
925
+ const i = this.state.get(e);
926
+ i.tempValues = [...i.activeValues], i.search = "", this.repaintOpts(e), this.repaintChips(e);
927
+ const a = s.querySelector(`#ds-fp-drop-${e}`);
928
+ a && (a.style.display = "none");
929
+ }
930
+ closePopover(e) {
931
+ const t = this.container.querySelector(`#ds-fp-pop-${e}`);
932
+ t && (t.style.display = "none"), this.activePopoverId === e && (this.activePopoverId = null);
933
+ }
934
+ closePopoverOnOutsideClick() {
935
+ document.addEventListener("mousedown", (e) => {
936
+ if (!this.activePopoverId) return;
937
+ const t = this.container.querySelector(`#ds-fp-pop-${this.activePopoverId}`), s = this.container.querySelector("#ds-fp-v3-bar");
938
+ t && !t.contains(e.target) && s && !s.contains(e.target) && this.closePopover(this.activePopoverId);
939
+ }, { capture: !0 });
940
+ }
941
+ // ── Apply / clear ──────────────────────────────────────────────────────────
942
+ applyFilter(e) {
943
+ var o;
944
+ const t = this.state.get(e), s = this.filters.find((i) => i.filterId === e);
945
+ t.activeValues = [...t.tempValues];
946
+ let r;
947
+ s.type === "date-range" ? r = t.tempValues.length >= 2 && (t.tempValues[0] || t.tempValues[1]) ? { from: t.tempValues[0], to: t.tempValues[1] } : "" : s.type === "multi-select" || t.tempValues.length > 1 ? r = t.tempValues.length > 0 ? [...t.tempValues] : "" : r = (o = t.tempValues[0]) != null ? o : "", this.repaintBadge(e), this.closePopover(e), this.onFilterChange(e, r, t.operator);
948
+ }
949
+ clearFilter(e) {
950
+ const t = this.state.get(e);
951
+ t.tempValues = [], t.activeValues = [], this.repaintBadge(e), this.repaintOpts(e), this.repaintChips(e), this.closePopover(e), this.onFilterChange(e, "", void 0);
952
+ }
953
+ // ── Repaint helpers ────────────────────────────────────────────────────────
954
+ repaintBadge(e) {
955
+ const t = this.state.get(e), s = this.container.querySelector(`.ds-fp-v3-badge[data-filter-id="${e}"]`);
956
+ s && (t.activeValues.length > 0 ? s.classList.add("ds-fp-v3-badge-active") : s.classList.remove("ds-fp-v3-badge-active"));
957
+ }
958
+ repaintBadges() {
959
+ this.filters.forEach((e) => this.repaintBadge(e.filterId));
960
+ }
961
+ repaintOpts(e) {
962
+ const t = this.state.get(e);
963
+ if (!t) return;
964
+ const s = this.container.querySelector(`#ds-fp-opts-${e}`);
965
+ if (!s) return;
966
+ s.innerHTML = this.renderOpts(e, t);
967
+ const r = this.container.querySelector(`#ds-fp-pop-${e}`);
968
+ r && this.bindOpts(e, r);
969
+ }
970
+ refreshPopoverOpts(e) {
971
+ this.repaintOpts(e);
972
+ const t = this.state.get(e);
973
+ if (!t) return;
974
+ const s = this.container.querySelector(
975
+ `.ds-fp-v3-ms-trigger[data-filter-id="${e}"] .ds-fp-v3-ms-display`
976
+ );
977
+ s && (s.textContent = t.tempValues.length === 0 ? "Type to search..." : t.tempValues.length === 1 ? this.optLabel(e, t.tempValues[0]) : `${t.tempValues.length} selected`);
978
+ }
979
+ repaintChips(e) {
980
+ const t = this.state.get(e);
981
+ if (!t) return;
982
+ const s = this.container.querySelector(`#ds-fp-chips-${e}`);
983
+ if (!s) return;
984
+ if (t.tempValues.length === 0) {
985
+ s.innerHTML = "";
986
+ const i = this.container.querySelector(
987
+ `.ds-fp-v3-ms-trigger[data-filter-id="${e}"] .ds-fp-v3-ms-display`
988
+ );
989
+ i && (i.textContent = "Type to search...");
990
+ return;
991
+ }
992
+ const r = this.filters.find((i) => i.filterId === e);
993
+ s.innerHTML = t.tempValues.map((i) => `
994
+ <span class="ds-fp-v3-chip">
995
+ ${this.escText(this.optLabel(r.filterId, i))}
996
+ <button type="button" class="ds-fp-v3-chip-x"
997
+ data-val="${this.escAttr(i)}"
998
+ data-filter-id="${this.escAttr(e)}">✕</button>
999
+ </span>`).join(""), this.bindChips(e, s);
1000
+ const o = this.container.querySelector(
1001
+ `.ds-fp-v3-ms-trigger[data-filter-id="${e}"] .ds-fp-v3-ms-display`
1002
+ );
1003
+ o && (o.textContent = t.tempValues.length === 1 ? this.optLabel(e, t.tempValues[0]) : `${t.tempValues.length} selected`);
1004
+ }
1005
+ // ── Emit reset-all signal ──────────────────────────────────────────────────
1006
+ resetAll() {
1007
+ this.state.forEach((e) => {
1008
+ e.tempValues = [], e.activeValues = [];
1009
+ }), this.repaintBadges(), this.activePopoverId && this.closePopover(this.activePopoverId), this.onFilterChange("__reset_all__", null);
1010
+ }
1011
+ // ── Utils ──────────────────────────────────────────────────────────────────
1012
+ optLabel(e, t) {
1013
+ const s = this.state.get(e);
1014
+ if (!s) return t;
1015
+ const r = s.options.find((o) => (typeof o == "string" ? o : o.value) === t);
1016
+ return r ? typeof r == "string" ? r : r.label : t;
1017
+ }
724
1018
  escAttr(e) {
725
1019
  return String(e != null ? e : "").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
726
1020
  }
@@ -728,9 +1022,9 @@ class pe {
728
1022
  return String(e != null ? e : "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
729
1023
  }
730
1024
  }
731
- const H = class H {
1025
+ const N = class N {
732
1026
  constructor(e, t) {
733
- this.activePageId = "", this.activeTabId = "", this.activeFilters = {}, this.chartRenderers = /* @__PURE__ */ new Map(), this.tableRenderer = new le(), this.cardRenderer = new ce(), this.isLoadingTab = !1, this.root = e, this.opts = t, this.activeFilters = { ...t.initialFilters };
1027
+ this.activePageId = "", this.activeTabId = "", this.activeFilters = {}, this.activeFilterOperators = {}, this.activeLocalFilters = {}, this.chartRenderers = /* @__PURE__ */ new Map(), this.tableRenderer = new le(), this.cardRenderer = new ce(), this.isLoadingTab = !1, this.tooltipEl = null, this.root = e, this.opts = t, this.activeFilters = { ...t.initialFilters };
734
1028
  }
735
1029
  // ── Initial full render ───────────────────────────────────────────────────
736
1030
  render(e) {
@@ -752,45 +1046,45 @@ const H = class H {
752
1046
  this.chartRenderers.forEach((e) => e.destroy()), this.chartRenderers.clear(), this.root.innerHTML = "";
753
1047
  }
754
1048
  exportPDF() {
755
- var n, p;
1049
+ var p, n;
756
1050
  const e = this.root.querySelector("#ds-grid");
757
1051
  if (!e) return;
758
1052
  const t = this.root.style.height, s = this.root.style.overflow;
759
1053
  this.root.style.height = "auto", this.root.style.overflow = "visible";
760
- const i = this.root.querySelector("#ds-canvas"), r = (n = i == null ? void 0 : i.style.height) != null ? n : "", a = (p = i == null ? void 0 : i.style.overflow) != null ? p : "";
761
- i && (i.style.height = "auto", i.style.overflow = "visible");
762
- const o = Array.from(e.querySelectorAll(".ds-table-scroll")), l = o.map((f) => ({
763
- el: f,
764
- overflow: f.style.overflow,
765
- maxHeight: f.style.maxHeight,
766
- height: f.style.height
1054
+ const r = this.root.querySelector("#ds-canvas"), o = (p = r == null ? void 0 : r.style.height) != null ? p : "", i = (n = r == null ? void 0 : r.style.overflow) != null ? n : "";
1055
+ r && (r.style.height = "auto", r.style.overflow = "visible");
1056
+ const a = Array.from(e.querySelectorAll(".ds-table-scroll")), f = a.map((g) => ({
1057
+ el: g,
1058
+ overflow: g.style.overflow,
1059
+ maxHeight: g.style.maxHeight,
1060
+ height: g.style.height
767
1061
  }));
768
- o.forEach((f) => {
769
- f.style.overflow = "visible", f.style.maxHeight = "none", f.style.height = "auto";
1062
+ a.forEach((g) => {
1063
+ g.style.overflow = "visible", g.style.maxHeight = "none", g.style.height = "auto";
770
1064
  });
771
- const d = e.offsetWidth, h = parseInt(e.style.height, 10) || e.offsetHeight, b = e.getBoundingClientRect().top + window.scrollY;
1065
+ const h = e.offsetWidth, v = parseInt(e.style.height, 10) || e.offsetHeight, m = e.getBoundingClientRect().top + window.scrollY;
772
1066
  import("./html2canvas.esm-CzwMv54K.js").then(
773
- ({ default: f }) => f(e, {
1067
+ ({ default: g }) => g(e, {
774
1068
  scale: 2,
775
1069
  useCORS: !0,
776
1070
  allowTaint: !0,
777
1071
  logging: !1,
778
- width: d,
779
- height: h,
780
- windowWidth: d,
781
- windowHeight: h,
1072
+ width: h,
1073
+ height: v,
1074
+ windowWidth: h,
1075
+ windowHeight: v,
782
1076
  scrollX: 0,
783
- scrollY: -b
784
- }).then((m) => {
785
- this.root.style.height = t, this.root.style.overflow = s, i && (i.style.height = r, i.style.overflow = a), l.forEach(({ el: c, overflow: u, maxHeight: g, height: k }) => {
786
- c.style.overflow = u, c.style.maxHeight = g, c.style.height = k;
787
- }), import("./jspdf.es.min-d9hlG26J.js").then((c) => c.j).then(({ jsPDF: c }) => {
788
- const u = m.width / 2, g = m.height / 2, k = new c({
789
- orientation: u >= g ? "landscape" : "portrait",
1077
+ scrollY: -m
1078
+ }).then((u) => {
1079
+ this.root.style.height = t, this.root.style.overflow = s, r && (r.style.height = o, r.style.overflow = i), f.forEach(({ el: b, overflow: c, maxHeight: d, height: l }) => {
1080
+ b.style.overflow = c, b.style.maxHeight = d, b.style.height = l;
1081
+ }), import("./jspdf.es.min-d9hlG26J.js").then((b) => b.j).then(({ jsPDF: b }) => {
1082
+ const c = u.width / 2, d = u.height / 2, l = new b({
1083
+ orientation: c >= d ? "landscape" : "portrait",
790
1084
  unit: "px",
791
- format: [u, g]
1085
+ format: [c, d]
792
1086
  });
793
- k.addImage(m.toDataURL("image/png"), "PNG", 0, 0, u, g), k.save(`${this.renderData.dashboard.name || "dashboard"}.pdf`);
1087
+ l.addImage(u.toDataURL("image/png"), "PNG", 0, 0, c, d), l.save(`${this.renderData.dashboard.name || "dashboard"}.pdf`);
794
1088
  });
795
1089
  })
796
1090
  );
@@ -799,30 +1093,42 @@ const H = class H {
799
1093
  this.renderData.components.forEach((e) => {
800
1094
  const t = this.renderData.data[e.id];
801
1095
  if (!(t != null && t.length) || !["table", "bar", "line", "area"].includes(e.type)) return;
802
- const s = Object.keys(t[0]), i = [s.join(","), ...t.map(
803
- (a) => s.map((o) => {
804
- var l;
805
- return JSON.stringify((l = a[o]) != null ? l : "");
1096
+ const s = Object.keys(t[0]), r = [s.join(","), ...t.map(
1097
+ (i) => s.map((a) => {
1098
+ var f;
1099
+ return JSON.stringify((f = i[a]) != null ? f : "");
806
1100
  }).join(",")
807
1101
  )].join(`
808
1102
  `);
809
1103
  Object.assign(document.createElement("a"), {
810
- href: URL.createObjectURL(new Blob([i], { type: "text/csv" })),
1104
+ href: URL.createObjectURL(new Blob([r], { type: "text/csv" })),
811
1105
  download: `${e.title || e.id}.csv`
812
1106
  }).click();
813
1107
  });
814
1108
  }
1109
+ /** Returns a snapshot of the currently active filter values (used by DeepspotSDK onTabSwitch). */
1110
+ getActiveFilters() {
1111
+ return { ...this.activeFilters };
1112
+ }
1113
+ /** Returns a snapshot of the active filter operators (=, LIKE, IN, etc.) keyed by applyToField. */
1114
+ getActiveFilterOperators() {
1115
+ return { ...this.activeFilterOperators };
1116
+ }
1117
+ /** Returns local (component-scoped) filter values keyed by filterId. */
1118
+ getActiveLocalFilters() {
1119
+ return { ...this.activeLocalFilters };
1120
+ }
815
1121
  updateFilterValues(e) {
816
1122
  var t;
817
1123
  Object.assign(this.activeFilters, e), (t = this.filterRenderer) == null || t.updateValues(e);
818
1124
  }
819
1125
  // ── Shell ─────────────────────────────────────────────────────────────────
820
1126
  buildShell() {
821
- const e = this.opts.embedLevel, t = this.renderData.dashboard.pages, s = e === "dashboard" && t.length > 1, i = e === "page" || e === "dashboard";
1127
+ const e = this.opts.embedLevel, t = this.renderData.dashboard.pages, s = e === "dashboard" && t.length > 1, r = e === "page" || e === "dashboard";
822
1128
  this.root.innerHTML = `
823
1129
  ${this.opts.hideExport ? "" : '<div class="ds-toolbar" id="ds-toolbar"></div>'}
824
1130
  ${s ? '<nav class="ds-page-nav" id="ds-page-nav"></nav>' : ""}
825
- ${i ? '<nav class="ds-page-nav" id="ds-tab-nav"></nav>' : ""}
1131
+ ${r ? '<nav class="ds-page-nav" id="ds-tab-nav"></nav>' : ""}
826
1132
  <div id="ds-filter-container"></div>
827
1133
  <div class="ds-canvas" id="ds-canvas">
828
1134
  <div class="ds-grid" id="ds-grid"></div>
@@ -832,13 +1138,17 @@ const H = class H {
832
1138
  <div class="ds-embed-spinner"></div>
833
1139
  </div>
834
1140
  `, this.root.style.position = "relative", this.opts.hideExport || this.buildExportToolbar();
835
- const r = this.root.querySelector("#ds-filter-container");
836
- this.filterRenderer = new pe(r, (a, o) => {
837
- var b, n, p, f, m;
838
- const l = (b = this.renderData.filters) == null ? void 0 : b.find((c) => c.filterId === a), d = (n = this.renderData.components) == null ? void 0 : n.find(
839
- (c) => c.type === "inline-filter" && c.id === a
840
- ), h = (l == null ? void 0 : l.applyToField) || ((p = d == null ? void 0 : d.properties) == null ? void 0 : p.applyToField) || a;
841
- this.activeFilters[h] = o, (m = (f = this.opts).onFilterChange) == null || m.call(f, this.activeFilters), this.opts.onTabSwitch(this.activePageId, this.activeTabId);
1141
+ const o = this.root.querySelector("#ds-filter-container");
1142
+ this.filterRenderer = new ge(o, (i, a, f) => {
1143
+ var b, c, d, l, T, w, k, x, $, y, S;
1144
+ if (i === "__reset_all__") {
1145
+ this.activeFilters = {}, this.activeFilterOperators = {}, this.activeLocalFilters = {}, (c = (b = this.opts).onFilterChange) == null || c.call(b, this.activeFilters), this.opts.onTabSwitch(this.activePageId, this.activeTabId);
1146
+ return;
1147
+ }
1148
+ const h = (d = this.renderData.filters) == null ? void 0 : d.find((C) => C.filterId === i), v = (l = this.renderData.components) == null ? void 0 : l.find(
1149
+ (C) => C.type === "inline-filter" && C.id === i
1150
+ ), m = (h == null ? void 0 : h.applyToField) || ((T = v == null ? void 0 : v.properties) == null ? void 0 : T.applyToField) || i, p = (x = (k = h == null ? void 0 : h.columnMappings) != null ? k : (w = v == null ? void 0 : v.properties) == null ? void 0 : w.columnMappings) != null ? x : {}, g = (($ = h == null ? void 0 : h.targetComponents) != null ? $ : []).length > 0 || Object.keys(p).length > 0, u = a === "" || a === null || a === void 0 || Array.isArray(a) && a.length === 0;
1151
+ g ? u ? delete this.activeLocalFilters[i] : this.activeLocalFilters[i] = { value: a, operator: f || "=" } : u ? (delete this.activeFilters[m], delete this.activeFilterOperators[m]) : (this.activeFilters[m] = a, f ? this.activeFilterOperators[m] = f : delete this.activeFilterOperators[m]), (S = (y = this.opts).onFilterChange) == null || S.call(y, this.activeFilters), this.opts.onTabSwitch(this.activePageId, this.activeTabId);
842
1152
  });
843
1153
  }
844
1154
  buildExportToolbar() {
@@ -860,9 +1170,9 @@ const H = class H {
860
1170
  data-page-id="${t.pageId}">${t.title}</button>
861
1171
  `).join(""), e.querySelectorAll(".ds-page-tab").forEach((t) => {
862
1172
  t.addEventListener("click", () => {
863
- var i, r;
1173
+ var r, o;
864
1174
  const s = t.dataset.pageId;
865
- s === this.activePageId && !this.isLoadingTab || this.triggerTabSwitch(s, (r = (i = this.getTabsForPage(s)[0]) == null ? void 0 : i.id) != null ? r : "");
1175
+ s === this.activePageId && !this.isLoadingTab || this.triggerTabSwitch(s, (o = (r = this.getTabsForPage(s)[0]) == null ? void 0 : r.id) != null ? o : "");
866
1176
  });
867
1177
  }));
868
1178
  }
@@ -876,13 +1186,13 @@ const H = class H {
876
1186
  e.style.display = "none";
877
1187
  return;
878
1188
  }
879
- e.style.display = "", e.innerHTML = s.map((i) => `
880
- <button class="ds-page-tab ${i.id === this.activeTabId ? "ds-active" : ""}"
881
- data-tab-id="${i.id}" data-page-id="${t}">${i.title}</button>
882
- `).join(""), e.querySelectorAll(".ds-page-tab").forEach((i) => {
883
- i.addEventListener("click", () => {
884
- const r = i.dataset.tabId, a = i.dataset.pageId;
885
- r === this.activeTabId && a === this.activePageId && !this.isLoadingTab || this.triggerTabSwitch(a, r);
1189
+ e.style.display = "", e.innerHTML = s.map((r) => `
1190
+ <button class="ds-page-tab ${r.id === this.activeTabId ? "ds-active" : ""}"
1191
+ data-tab-id="${r.id}" data-page-id="${t}">${r.title}</button>
1192
+ `).join(""), e.querySelectorAll(".ds-page-tab").forEach((r) => {
1193
+ r.addEventListener("click", () => {
1194
+ const o = r.dataset.tabId, i = r.dataset.pageId;
1195
+ o === this.activeTabId && i === this.activePageId && !this.isLoadingTab || this.triggerTabSwitch(i, o);
886
1196
  });
887
1197
  });
888
1198
  }
@@ -901,88 +1211,133 @@ const H = class H {
901
1211
  // components (type='inline-filter' in renderData.components[]).
902
1212
  // Inline-filter components are NOT rendered in the grid — they appear here.
903
1213
  renderFilterBar() {
904
- if (this.opts.hideGlobalFilters) return;
905
- const e = [
906
- ...this.renderData.filters || [],
907
- ...this.inlineFiltersAsDefinitions()
908
- ];
909
- this.filterRenderer.render(e, this.activeFilters), this.fetchFilterOptions();
1214
+ this.opts.hideGlobalFilters || (this.filterRenderer.render(this.globalFilters(), this.mergedFilterValues()), this.fetchFilterOptions());
910
1215
  }
911
1216
  updateFilterBar() {
912
- if (this.opts.hideGlobalFilters) return;
913
- const e = [
914
- ...this.renderData.filters || [],
915
- ...this.inlineFiltersAsDefinitions()
916
- ];
917
- this.filterRenderer.render(e, this.activeFilters), this.fetchFilterOptions();
918
- }
919
- /** Convert inline-filter ComponentConfig entries to FilterDefinition shape */
1217
+ this.opts.hideGlobalFilters || (this.filterRenderer.render(this.globalFilters(), this.mergedFilterValues()), this.fetchFilterOptions());
1218
+ }
1219
+ /** Combines global activeFilters and local filter values (keyed by filterId)
1220
+ * so FilterRenderer can show correct active state for targeted filters. */
1221
+ mergedFilterValues() {
1222
+ const e = { ...this.activeFilters };
1223
+ for (const [t, { value: s }] of Object.entries(this.activeLocalFilters))
1224
+ e[t] = s;
1225
+ return e;
1226
+ }
1227
+ /**
1228
+ * Returns true when a filter should appear in the global top filter bar.
1229
+ * A filter is global when the matching inline-filter component has
1230
+ * filter_scope/scope !== 'local' and filterLevel !== 'component'.
1231
+ */
1232
+ filterIsGlobal(e) {
1233
+ var r, o;
1234
+ const t = (this.renderData.components || []).find(
1235
+ (i) => i.type === "inline-filter" && i.id === e
1236
+ );
1237
+ if (t) {
1238
+ const i = ((r = t.properties) == null ? void 0 : r.filter_scope) || ((o = t.properties) == null ? void 0 : o.scope);
1239
+ if (i === "local") return !1;
1240
+ if (i === "global") return !0;
1241
+ }
1242
+ const s = (this.renderData.filters || []).find((i) => i.filterId === e);
1243
+ return !(s != null && s.filterLevel) || s.filterLevel === "global";
1244
+ }
1245
+ /** Filters from renderData.filters[] that should appear in the global top panel. */
1246
+ globalFilters() {
1247
+ return (this.renderData.filters || []).filter((e) => this.filterIsGlobal(e.filterId));
1248
+ }
1249
+ /**
1250
+ * Filters that are component-scoped (filter_scope='local').
1251
+ * Source of truth is filters[] — which has applyToField, targetComponents, etc.
1252
+ * The matching inline-filter component in components[] provides the scope signal.
1253
+ */
920
1254
  inlineFiltersAsDefinitions() {
921
- return this.opts.hideInlineFilters ? [] : (this.renderData.components || []).filter((e) => e.type === "inline-filter").map((e) => {
922
- var t, s, i, r, a, o, l, d;
923
- return {
924
- filterId: e.id,
925
- label: ((t = e.properties) == null ? void 0 : t.filterLabel) || e.title || "",
926
- type: ((s = e.properties) == null ? void 0 : s.filterType) || "dropdown",
927
- options: ((i = e.properties) == null ? void 0 : i.filterOptions) || ((r = e.properties) == null ? void 0 : r.options) || [],
928
- defaultValue: (a = e.properties) == null ? void 0 : a.defaultValue,
929
- targetComponents: ((o = e.properties) == null ? void 0 : o.targetComponents) || [],
930
- filterOperator: (l = e.properties) == null ? void 0 : l.filterOperator,
931
- applyToField: ((d = e.properties) == null ? void 0 : d.applyToField) || e.id
932
- };
933
- });
1255
+ return this.opts.hideInlineFilters ? [] : (this.renderData.filters || []).filter((e) => {
1256
+ var t, s;
1257
+ return !this.filterIsGlobal(e.filterId) && ((s = (t = e.targetComponents) == null ? void 0 : t.length) != null ? s : 0) > 0;
1258
+ }).map((e) => ({ ...e, filterLevel: "component" }));
934
1259
  }
935
1260
  async fetchFilterOptions() {
936
- if (this.opts.onFetchFilterOptions)
937
- for (const e of this.renderData.filters) {
938
- if (e.type !== "dropdown" && e.type !== "multi-select" || e.options && e.options.length > 0) continue;
939
- const t = await this.opts.onFetchFilterOptions(e.filterId);
940
- t.length > 0 && this.filterRenderer.updateOptions(e.filterId, t);
941
- }
1261
+ if (this.opts.onFetchFilterOptions) {
1262
+ for (const e of this.globalFilters())
1263
+ if (!(e.type !== "dropdown" && e.type !== "multi-select") && !(e.options && e.options.length > 0)) {
1264
+ this.filterRenderer.setLoading(e.filterId, !0);
1265
+ try {
1266
+ const t = await this.opts.onFetchFilterOptions(e.filterId);
1267
+ this.filterRenderer.updateOptions(e.filterId, t);
1268
+ } catch (t) {
1269
+ this.filterRenderer.setLoading(e.filterId, !1);
1270
+ }
1271
+ }
1272
+ }
942
1273
  }
943
1274
  // ── Grid ──────────────────────────────────────────────────────────────────
944
1275
  renderGrid() {
1276
+ var m;
945
1277
  this.hideTabSpinner();
946
1278
  const e = this.root.querySelector("#ds-grid");
947
1279
  if (!e) return;
948
1280
  if (!this.opts.hideBackground) {
949
- const d = this.root.querySelector("#ds-canvas"), h = this.renderData.dashboard.pages.find(
950
- (n) => n.pageId === this.activePageId
951
- ), b = (h == null ? void 0 : h.backgroundColor) || "";
952
- d && (d.style.background = b), this.root.style.background = b;
1281
+ const p = this.root.querySelector("#ds-canvas"), n = this.renderData.dashboard.pages.find(
1282
+ (u) => u.pageId === this.activePageId
1283
+ ), g = (n == null ? void 0 : n.backgroundColor) || "";
1284
+ p && (p.style.background = g), this.root.style.background = g;
953
1285
  }
954
- this.chartRenderers.forEach((d) => d.destroy()), this.chartRenderers.clear();
1286
+ this.chartRenderers.forEach((p) => p.destroy()), this.chartRenderers.clear();
955
1287
  const t = (this.renderData.components || []).filter(
956
- (d) => d.type !== "inline-filter"
1288
+ (p) => p.type !== "inline-filter"
957
1289
  );
958
1290
  if (!t.length) {
959
1291
  e.style.height = "200px", e.innerHTML = '<div class="ds-chart-empty" style="padding-top:80px;">No components on this tab.</div>';
960
1292
  return;
961
1293
  }
962
- const s = e.clientWidth || this.root.clientWidth || 800, i = t.map((d) => d.position), r = i.length ? Math.min(...i.map((d) => d.y)) : 0, a = new ae(s, r);
963
- e.style.height = `${ae.totalHeight(i)}px`, e.innerHTML = "";
964
- const o = 36, l = [];
965
- t.forEach((d) => {
966
- var p, f, m;
967
- const h = document.createElement("div");
968
- if (h.className = "ds-component-wrapper", h.dataset.componentId = d.id, a.applyStyles(h, d.position), !this.opts.hideBackground) {
969
- const c = ((f = (p = d.properties) == null ? void 0 : p.styleConfig) == null ? void 0 : f.backgroundColor) || ((m = d.properties) == null ? void 0 : m.backgroundColor);
970
- c && (h.style.background = c);
1294
+ const s = e.clientWidth || this.root.clientWidth || 800, r = t.map((p) => p.position), o = r.length ? Math.min(...r.map((p) => p.y)) : 0, i = new ie(s, o);
1295
+ e.style.height = `${ie.totalHeight(r)}px`, e.innerHTML = "";
1296
+ const a = 36, f = this.inlineFiltersAsDefinitions(), h = /* @__PURE__ */ new Map();
1297
+ for (const p of f)
1298
+ for (const n of (m = p.targetComponents) != null ? m : [])
1299
+ h.has(n) || h.set(n, []), h.get(n).push(p);
1300
+ const v = [];
1301
+ t.forEach((p) => {
1302
+ var d, l, T, w, k;
1303
+ const n = document.createElement("div");
1304
+ if (n.className = "ds-component-wrapper", n.dataset.componentId = p.id, i.applyStyles(n, p.position), !this.opts.hideBackground) {
1305
+ const x = ((l = (d = p.properties) == null ? void 0 : d.styleConfig) == null ? void 0 : l.backgroundColor) || ((T = p.properties) == null ? void 0 : T.backgroundColor);
1306
+ x && (n.style.background = x);
971
1307
  }
972
- e.appendChild(h);
973
- const b = a.toPx(d.position), n = Math.max(50, b.height - o);
974
- l.push(
975
- this.renderComponent(h, d, n).catch((c) => {
976
- console.error(`[Deepspot SDK] Failed to render component ${d.id} (${d.type}):`, c), h.innerHTML = `<div style="padding:8px;color:#ef4444;font-size:12px;">⚠ ${d.type} render error</div>`;
1308
+ e.appendChild(n);
1309
+ const g = i.toPx(p.position), u = Math.max(50, g.height - a), b = document.createElement("div");
1310
+ b.style.cssText = "width:100%;height:100%;", n.appendChild(b);
1311
+ const c = this.getComponentDescription(p);
1312
+ if (c) {
1313
+ const x = this.getTooltipEl();
1314
+ n.addEventListener("mouseenter", () => {
1315
+ x.textContent = c, x.style.display = "block";
1316
+ const $ = n.getBoundingClientRect(), y = x.offsetHeight || 36;
1317
+ window.innerHeight - $.bottom >= y + 6 ? x.style.top = `${$.bottom + 4}px` : x.style.top = `${$.top - y - 4}px`, x.style.left = `${$.left}px`, x.style.width = `${$.width}px`;
1318
+ }), n.addEventListener("mouseleave", () => {
1319
+ x.style.display = "none";
1320
+ });
1321
+ }
1322
+ if (!this.opts.hideInlineFilters) {
1323
+ const x = [
1324
+ ...(w = h.get(p.id)) != null ? w : [],
1325
+ ...(k = h.get("__all__")) != null ? k : []
1326
+ ].filter(($, y, S) => S.findIndex((C) => C.filterId === $.filterId) === y);
1327
+ x.length > 0 && this.attachInlineFilterBtn(n, x);
1328
+ }
1329
+ v.push(
1330
+ this.renderComponent(b, p, u).catch((x) => {
1331
+ console.error(`[Deepspot SDK] Failed to render component ${p.id} (${p.type}):`, x), b.innerHTML = `<div style="padding:8px;color:#ef4444;font-size:12px;">⚠ ${p.type} render error</div>`;
977
1332
  })
978
1333
  );
979
- }), Promise.all(l).catch(() => {
1334
+ }), Promise.all(v).catch(() => {
980
1335
  });
981
1336
  }
982
1337
  // ── Component renderer switch ─────────────────────────────────────────────
983
1338
  async renderComponent(e, t, s) {
984
- var a, o, l, d, h, b, n, p, f, m, c, u, g, k, T, C, $, R, I, P, L, U, W, _, Y, J, X, Z, Q, V, ee, te, se, ie, re;
985
- const i = this.renderData.data[t.id] || [], r = this.opts.theme;
1339
+ var i, a, f, h, v, m, p, n, g, u, b, c, d, l, T, w, k, x, $, y, S, C, P, U, W, Y, J, X, Z, Q, ee, te, se, re, oe;
1340
+ const r = this.renderData.data[t.id] || [], o = this.opts.theme;
986
1341
  switch (t.type) {
987
1342
  case "bar":
988
1343
  case "line":
@@ -991,82 +1346,307 @@ const H = class H {
991
1346
  case "area":
992
1347
  case "scatter":
993
1348
  case "stacked-bar": {
994
- const v = new ne(e);
995
- await v.render(t, i, r, s), this.chartRenderers.set(t.id, v);
1349
+ const F = new ne(e);
1350
+ await F.render(t, r, o, s), this.chartRenderers.set(t.id, F);
996
1351
  break;
997
1352
  }
998
1353
  case "table": {
999
- const v = this.opts.onFetchTablePage ? (x, S) => this.opts.onFetchTablePage(t.id, x, S) : void 0;
1000
- this.tableRenderer.render(e, t, i, v);
1354
+ const F = this.opts.onFetchTablePage ? (L, E) => this.opts.onFetchTablePage(t.id, L, E) : void 0;
1355
+ this.tableRenderer.render(e, t, r, F);
1001
1356
  break;
1002
1357
  }
1003
1358
  case "number-card":
1004
- this.cardRenderer.render(e, t, i);
1359
+ this.cardRenderer.render(e, t, r);
1005
1360
  break;
1006
1361
  case "text-heading": {
1007
1362
  if (this.opts.hideText) break;
1008
- const v = ((o = (a = t.properties) == null ? void 0 : a.data) == null ? void 0 : o.dataContent) || ((l = t.properties) == null ? void 0 : l.content) || t.title || "", x = ((d = t.properties) == null ? void 0 : d.styleConfig) || {}, S = this.scToInlineStyle(x), y = this.opts.hideBackground ? {} : this.parseTwColor((b = (h = t.properties) == null ? void 0 : h.color) != null ? b : ""), F = [[
1009
- y.bg ? `background:${y.bg}` : "",
1010
- y.text ? `color:${y.text}` : ""
1011
- ].filter(Boolean).join(";"), S].filter(Boolean).join(";");
1012
- e.innerHTML = `<div class="ds-text-heading" style="${F}">${v}</div>`;
1363
+ const F = ((a = (i = t.properties) == null ? void 0 : i.data) == null ? void 0 : a.dataContent) || ((f = t.properties) == null ? void 0 : f.content) || t.title || "", L = ((h = t.properties) == null ? void 0 : h.styleConfig) || {}, E = this.scToInlineStyle(L), I = this.opts.hideBackground ? {} : this.parseTwColor((m = (v = t.properties) == null ? void 0 : v.color) != null ? m : ""), R = [[
1364
+ I.bg ? `background:${I.bg}` : "",
1365
+ I.text ? `color:${I.text}` : ""
1366
+ ].filter(Boolean).join(";"), E].filter(Boolean).join(";");
1367
+ e.innerHTML = `<div class="ds-text-heading" style="${R}">${F}</div>`;
1013
1368
  break;
1014
1369
  }
1015
1370
  case "text-subheading": {
1016
1371
  if (this.opts.hideText) break;
1017
- const v = ((p = (n = t.properties) == null ? void 0 : n.data) == null ? void 0 : p.dataContent) || ((f = t.properties) == null ? void 0 : f.content) || t.title || "", x = ((m = t.properties) == null ? void 0 : m.styleConfig) || {}, S = this.scToInlineStyle(x), y = this.opts.hideBackground ? {} : this.parseTwColor((u = (c = t.properties) == null ? void 0 : c.color) != null ? u : ""), F = [[
1018
- y.bg ? `background:${y.bg}` : "",
1019
- y.text ? `color:${y.text}` : ""
1020
- ].filter(Boolean).join(";"), S].filter(Boolean).join(";");
1021
- e.innerHTML = `<div class="ds-text-subheading" style="${F}">${v}</div>`;
1372
+ const F = ((n = (p = t.properties) == null ? void 0 : p.data) == null ? void 0 : n.dataContent) || ((g = t.properties) == null ? void 0 : g.content) || t.title || "", L = ((u = t.properties) == null ? void 0 : u.styleConfig) || {}, E = this.scToInlineStyle(L), I = this.opts.hideBackground ? {} : this.parseTwColor((c = (b = t.properties) == null ? void 0 : b.color) != null ? c : ""), R = [[
1373
+ I.bg ? `background:${I.bg}` : "",
1374
+ I.text ? `color:${I.text}` : ""
1375
+ ].filter(Boolean).join(";"), E].filter(Boolean).join(";");
1376
+ e.innerHTML = `<div class="ds-text-subheading" style="${R}">${F}</div>`;
1022
1377
  break;
1023
1378
  }
1024
1379
  case "text-body": {
1025
1380
  if (this.opts.hideText) break;
1026
- const v = ((k = (g = t.properties) == null ? void 0 : g.data) == null ? void 0 : k.dataContent) || ((T = t.properties) == null ? void 0 : T.content) || "", x = ((C = t.properties) == null ? void 0 : C.styleConfig) || {}, S = this.scToInlineStyle(x), y = this.opts.hideBackground ? {} : this.parseTwColor((R = ($ = t.properties) == null ? void 0 : $.color) != null ? R : ""), F = [[
1027
- y.bg ? `background:${y.bg}` : "",
1028
- y.text ? `color:${y.text}` : ""
1029
- ].filter(Boolean).join(";"), S].filter(Boolean).join(";");
1030
- e.innerHTML = `<div class="ds-text-body" style="${F}">${v}</div>`;
1381
+ const F = ((l = (d = t.properties) == null ? void 0 : d.data) == null ? void 0 : l.dataContent) || ((T = t.properties) == null ? void 0 : T.content) || "", L = ((w = t.properties) == null ? void 0 : w.styleConfig) || {}, E = this.scToInlineStyle(L), I = this.opts.hideBackground ? {} : this.parseTwColor((x = (k = t.properties) == null ? void 0 : k.color) != null ? x : ""), R = [[
1382
+ I.bg ? `background:${I.bg}` : "",
1383
+ I.text ? `color:${I.text}` : ""
1384
+ ].filter(Boolean).join(";"), E].filter(Boolean).join(";");
1385
+ e.innerHTML = `<div class="ds-text-body" style="${R}">${F}</div>`;
1031
1386
  break;
1032
1387
  }
1033
1388
  case "text-box": {
1034
1389
  if (this.opts.hideText) break;
1035
- const v = ((P = (I = t.properties) == null ? void 0 : I.data) == null ? void 0 : P.dataContent) || ((L = t.properties) == null ? void 0 : L.content) || "", x = ((U = t.properties) == null ? void 0 : U.styleConfig) || {}, S = this.scToInlineStyle(x), y = this.opts.hideBackground ? {} : this.parseTwColor((_ = (W = t.properties) == null ? void 0 : W.color) != null ? _ : ""), F = [[
1036
- y.bg ? `background:${y.bg}` : "",
1037
- y.text ? `color:${y.text}` : ""
1038
- ].filter(Boolean).join(";"), S].filter(Boolean).join(";");
1039
- e.innerHTML = `<div class="ds-text-box" style="${F}">${v}</div>`;
1390
+ const F = ((y = ($ = t.properties) == null ? void 0 : $.data) == null ? void 0 : y.dataContent) || ((S = t.properties) == null ? void 0 : S.content) || "", L = ((C = t.properties) == null ? void 0 : C.styleConfig) || {}, E = this.scToInlineStyle(L), I = this.opts.hideBackground ? {} : this.parseTwColor((U = (P = t.properties) == null ? void 0 : P.color) != null ? U : ""), R = [[
1391
+ I.bg ? `background:${I.bg}` : "",
1392
+ I.text ? `color:${I.text}` : ""
1393
+ ].filter(Boolean).join(";"), E].filter(Boolean).join(";");
1394
+ e.innerHTML = `<div class="ds-text-box" style="${R}">${F}</div>`;
1040
1395
  break;
1041
1396
  }
1042
1397
  case "text": {
1043
1398
  if (this.opts.hideText) break;
1044
- const v = ((J = (Y = t.properties) == null ? void 0 : Y.data) == null ? void 0 : J.dataContent) || ((X = t.properties) == null ? void 0 : X.content) || "", x = ((Z = t.properties) == null ? void 0 : Z.styleConfig) || {}, S = this.scToInlineStyle(x), y = this.opts.hideBackground ? {} : this.parseTwColor((V = (Q = t.properties) == null ? void 0 : Q.color) != null ? V : ""), F = [[
1045
- y.bg ? `background:${y.bg}` : "",
1046
- y.text ? `color:${y.text}` : ""
1047
- ].filter(Boolean).join(";"), S].filter(Boolean).join(";");
1048
- e.innerHTML = `<div class="ds-text-body" style="${F}">${v}</div>`;
1399
+ const F = ((Y = (W = t.properties) == null ? void 0 : W.data) == null ? void 0 : Y.dataContent) || ((J = t.properties) == null ? void 0 : J.content) || "", L = ((X = t.properties) == null ? void 0 : X.styleConfig) || {}, E = this.scToInlineStyle(L), I = this.opts.hideBackground ? {} : this.parseTwColor((Q = (Z = t.properties) == null ? void 0 : Z.color) != null ? Q : ""), R = [[
1400
+ I.bg ? `background:${I.bg}` : "",
1401
+ I.text ? `color:${I.text}` : ""
1402
+ ].filter(Boolean).join(";"), E].filter(Boolean).join(";");
1403
+ e.innerHTML = `<div class="ds-text-body" style="${R}">${F}</div>`;
1049
1404
  break;
1050
1405
  }
1051
1406
  case "header": {
1052
1407
  if (this.opts.hideHeader) break;
1053
- const v = ((te = (ee = t.properties) == null ? void 0 : ee.data) == null ? void 0 : te.dataContent) || ((se = t.properties) == null ? void 0 : se.content) || t.title || "", x = ((ie = t.properties) == null ? void 0 : ie.styleConfig) || {}, S = !this.opts.hideBackground && x.backgroundColor ? `background:${x.backgroundColor};` : "", y = x.color ? `color:${x.color};` : x.textColor ? `color:${x.textColor};` : "";
1408
+ const F = ((te = (ee = t.properties) == null ? void 0 : ee.data) == null ? void 0 : te.dataContent) || ((se = t.properties) == null ? void 0 : se.content) || t.title || "", L = ((re = t.properties) == null ? void 0 : re.styleConfig) || {}, E = !this.opts.hideBackground && L.backgroundColor ? `background:${L.backgroundColor};` : "", I = L.color ? `color:${L.color};` : L.textColor ? `color:${L.textColor};` : "";
1054
1409
  e.innerHTML = `
1055
- <div class="ds-header-component" style="${S}${y}">
1056
- <div class="ds-header-content">${v}</div>
1410
+ <div class="ds-header-component" style="${E}${I}">
1411
+ <div class="ds-header-content">${F}</div>
1057
1412
  </div>`;
1058
1413
  break;
1059
1414
  }
1060
1415
  case "section": {
1061
- const v = ((re = t.properties) == null ? void 0 : re.styleConfig) || {}, x = !this.opts.hideBackground && v.backgroundColor ? `background:${v.backgroundColor};` : "";
1416
+ const F = ((oe = t.properties) == null ? void 0 : oe.styleConfig) || {}, L = !this.opts.hideBackground && F.backgroundColor ? `background:${F.backgroundColor};` : "";
1062
1417
  e.innerHTML = `
1063
- <div class="ds-section-wrapper" style="${x}">
1418
+ <div class="ds-section-wrapper" style="${L}">
1064
1419
  ${t.title ? `<div class="ds-section-title">${t.title}</div>` : ""}
1065
1420
  </div>`;
1066
1421
  break;
1067
1422
  }
1068
1423
  }
1069
1424
  }
1425
+ // ── Inline-filter funnel button on chart wrappers ────────────────────────
1426
+ attachInlineFilterBtn(e, t) {
1427
+ var a;
1428
+ e.style.overflow = "visible";
1429
+ const s = (a = e.dataset.componentId) != null ? a : "", r = document.createElement("button");
1430
+ r.type = "button", r.className = "ds-comp-filter-btn", r.title = "Filter", r.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
1431
+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1432
+ <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
1433
+ </svg>`;
1434
+ const o = document.createElement("div");
1435
+ o.className = "ds-comp-filter-pop", o.style.display = "none", this.buildInlineFilterPop(o, t, s), r.addEventListener("click", (f) => {
1436
+ f.stopPropagation();
1437
+ const h = o.style.display !== "none";
1438
+ document.querySelectorAll(".ds-comp-filter-pop").forEach((v) => {
1439
+ v.style.display = "none";
1440
+ }), o.style.display = h ? "none" : "";
1441
+ }), document.addEventListener("mousedown", (f) => {
1442
+ e.contains(f.target) || (o.style.display = "none");
1443
+ }), (() => {
1444
+ const f = t.some((h) => {
1445
+ const v = h.applyToField || h.filterId, m = this.activeFilters[v] !== void 0 && this.activeFilters[v] !== "", p = this.activeLocalFilters[h.filterId] !== void 0;
1446
+ return m || p;
1447
+ });
1448
+ r.classList.toggle("ds-comp-filter-btn-active", f);
1449
+ })(), e.appendChild(r), e.appendChild(o);
1450
+ }
1451
+ buildInlineFilterPop(e, t, s) {
1452
+ var g, u, b;
1453
+ const r = (c) => {
1454
+ var T;
1455
+ const d = (this.renderData.components || []).find(
1456
+ (w) => w.type === "inline-filter" && w.id === c.filterId
1457
+ ), l = (T = d == null ? void 0 : d.properties) == null ? void 0 : T.columnMappings;
1458
+ return (l == null ? void 0 : l[s]) || c.applyToField || c.filterId;
1459
+ }, o = (c) => c.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"), i = (c) => c.replace(/"/g, "&quot;").replace(/'/g, "&#39;"), a = [
1460
+ { value: "=", label: "Equals (=)" },
1461
+ { value: "!=", label: "Not Equals (!=)" },
1462
+ { value: "LIKE", label: "Contains" },
1463
+ { value: "NOT LIKE", label: "Not Contains" },
1464
+ { value: "STARTS_WITH", label: "Starts With" },
1465
+ { value: "IN", label: "In (Multiple)" },
1466
+ { value: "NOT IN", label: "Not In" },
1467
+ { value: ">", label: "Greater Than (>)" },
1468
+ { value: ">=", label: "Greater or Equal (>=)" },
1469
+ { value: "<", label: "Less Than (<)" },
1470
+ { value: "<=", label: "Less or Equal (<=)" },
1471
+ { value: "IS NULL", label: "Is Empty" },
1472
+ { value: "IS NOT NULL", label: "Is Not Empty" }
1473
+ ], f = /* @__PURE__ */ new Map();
1474
+ for (const c of t) {
1475
+ const d = this.activeLocalFilters[c.filterId], l = (g = d == null ? void 0 : d.value) != null ? g : this.activeFilters[r(c)], T = l ? Array.isArray(l) ? l.map(String) : [String(l)] : [], w = (d == null ? void 0 : d.operator) || c.filterOperator || "=";
1476
+ f.set(c.filterId, { pendingValues: [...T], operator: w, search: "" });
1477
+ }
1478
+ const h = (c) => `<select class="ds-fp-op-select">
1479
+ ${a.map(
1480
+ (d) => `<option value="${i(d.value)}"${d.value === c ? " selected" : ""}>${d.label}</option>`
1481
+ ).join("")}
1482
+ </select>`, v = (c, d) => {
1483
+ const l = c.options || [];
1484
+ if (!l.length)
1485
+ return '<div class="ds-fp-opts-msg"><span class="ds-dd-spinner"></span> Loading…</div>';
1486
+ const T = d.search.toLowerCase(), w = l.filter(
1487
+ (k) => (typeof k == "string" ? k : k.label).toLowerCase().includes(T)
1488
+ );
1489
+ return w.length ? w.map((k) => {
1490
+ const x = typeof k == "string" ? k : k.value, $ = typeof k == "string" ? k : k.label, y = d.pendingValues.includes(x);
1491
+ return `<label class="ds-fp-opt${y ? " ds-fp-opt-on" : ""}" data-val="${i(x)}">
1492
+ <span class="ds-fp-cb${y ? " ds-fp-cb-on" : ""}">
1493
+ ${y ? `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"
1494
+ stroke-linecap="round" stroke-linejoin="round">
1495
+ <polyline points="20 6 9 17 4 12"/>
1496
+ </svg>` : ""}
1497
+ </span>
1498
+ <span class="ds-fp-opt-lbl">${o($)}</span>
1499
+ </label>`;
1500
+ }).join("") : '<div class="ds-fp-opts-msg">No matches</div>';
1501
+ }, m = (c) => {
1502
+ const d = f.get(c.filterId);
1503
+ return `
1504
+ <div class="ds-fp-item ds-fp-item-expanded" data-filter-id="${i(c.filterId)}">
1505
+ <div class="ds-fp-item-hd">
1506
+ <span class="ds-fp-item-name">${o(c.label)}</span>
1507
+ <svg class="ds-fp-chevron" style="transform:rotate(180deg)"
1508
+ viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
1509
+ stroke-linecap="round" stroke-linejoin="round">
1510
+ <polyline points="6 9 12 15 18 9"/>
1511
+ </svg>
1512
+ </div>
1513
+ <div class="ds-fp-item-body">
1514
+ <div class="ds-fp-op-row">
1515
+ <span class="ds-fp-op-lbl">Operator</span>
1516
+ ${h(d.operator)}
1517
+ </div>
1518
+ <div class="ds-fp-ms">
1519
+ ${d.pendingValues.length ? `<div class="ds-fp-chips-row">
1520
+ ${d.pendingValues.map(
1521
+ (l) => `<span class="ds-fp-chip">${o(l)}<button type="button" class="ds-fp-chip-x" data-val="${i(l)}">✕</button></span>`
1522
+ ).join("")}
1523
+ </div>` : ""}
1524
+ <div class="ds-fp-search-wrap">
1525
+ <input type="text" class="ds-fp-ms-search"
1526
+ placeholder="Search ${i(c.label)}…"
1527
+ value="${i(d.search)}"/>
1528
+ <svg class="ds-fp-search-caret" viewBox="0 0 24 24" fill="none" stroke="currentColor"
1529
+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1530
+ <polyline points="18 15 12 9 6 15"/>
1531
+ </svg>
1532
+ </div>
1533
+ <div class="ds-fp-opts" data-filter-id="${i(c.filterId)}">
1534
+ ${v(c, d)}
1535
+ </div>
1536
+ </div>
1537
+ <button type="button" class="ds-fp-apply" data-apply-id="${i(c.filterId)}">✓ Apply Filter</button>
1538
+ </div>
1539
+ </div>`;
1540
+ }, p = t.filter((c) => {
1541
+ if (this.activeLocalFilters[c.filterId] !== void 0) return !0;
1542
+ const d = this.activeFilters[r(c)];
1543
+ return d !== void 0 && d !== "" && !(Array.isArray(d) && d.length === 0);
1544
+ }).length;
1545
+ e.innerHTML = `
1546
+ <div class="ds-fp-panel ds-fp-panel-pop">
1547
+ <div class="ds-fp-panel-hd">
1548
+ <div class="ds-fp-panel-title">
1549
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
1550
+ stroke-linecap="round" stroke-linejoin="round">
1551
+ <line x1="3" y1="6" x2="21" y2="6"/>
1552
+ <line x1="3" y1="12" x2="21" y2="12"/>
1553
+ <line x1="3" y1="18" x2="21" y2="18"/>
1554
+ </svg>
1555
+ <span>Filters</span>
1556
+ </div>
1557
+ <button type="button" class="ds-fp-close-btn ds-pop-close">
1558
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
1559
+ stroke-linecap="round" stroke-linejoin="round">
1560
+ <line x1="18" y1="6" x2="6" y2="18"/>
1561
+ <line x1="6" y1="6" x2="18" y2="18"/>
1562
+ </svg>
1563
+ </button>
1564
+ </div>
1565
+ <div class="ds-fp-reset-section" style="display:${p ? "" : "none"}">
1566
+ <button type="button" class="ds-fp-reset-all ds-pop-reset">
1567
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
1568
+ stroke-linecap="round" stroke-linejoin="round">
1569
+ <polyline points="1 4 1 10 7 10"/>
1570
+ <path d="M3.51 15a9 9 0 1 0 .49-3.5"/>
1571
+ </svg>
1572
+ Reset All Filters
1573
+ </button>
1574
+ </div>
1575
+ <div class="ds-fp-items">
1576
+ ${t.map(m).join("")}
1577
+ </div>
1578
+ </div>`, (u = e.querySelector(".ds-pop-close")) == null || u.addEventListener("click", () => {
1579
+ e.style.display = "none";
1580
+ }), (b = e.querySelector(".ds-pop-reset")) == null || b.addEventListener("click", () => {
1581
+ var c, d;
1582
+ t.forEach((l) => {
1583
+ delete this.activeLocalFilters[l.filterId];
1584
+ }), (d = (c = this.opts).onFilterChange) == null || d.call(c, this.activeFilters), this.opts.onTabSwitch(this.activePageId, this.activeTabId), e.style.display = "none";
1585
+ });
1586
+ const n = (c, d, l) => {
1587
+ const T = t.find((x) => x.filterId === c), w = d.querySelector(".ds-fp-ms");
1588
+ if (w) {
1589
+ let x = w.querySelector(".ds-fp-chips-row");
1590
+ if (l.pendingValues.length) {
1591
+ const $ = l.pendingValues.map(
1592
+ (y) => `<span class="ds-fp-chip">${o(y)}<button type="button" class="ds-fp-chip-x" data-val="${i(y)}">✕</button></span>`
1593
+ ).join("");
1594
+ x ? x.innerHTML = $ : (x = document.createElement("div"), x.className = "ds-fp-chips-row", x.innerHTML = $, w.insertBefore(x, w.querySelector(".ds-fp-search-wrap"))), x.querySelectorAll(".ds-fp-chip-x").forEach((y) => {
1595
+ y.addEventListener("click", (S) => {
1596
+ S.stopPropagation(), l.pendingValues = l.pendingValues.filter((C) => {
1597
+ var P;
1598
+ return C !== ((P = y.dataset.val) != null ? P : "");
1599
+ }), n(c, d, l);
1600
+ });
1601
+ });
1602
+ } else
1603
+ x == null || x.remove();
1604
+ }
1605
+ const k = d.querySelector(`.ds-fp-opts[data-filter-id="${c}"]`);
1606
+ k && (k.innerHTML = v(T, l), k.querySelectorAll(".ds-fp-opt").forEach((x) => {
1607
+ x.addEventListener("click", () => {
1608
+ var y;
1609
+ const $ = (y = x.dataset.val) != null ? y : "";
1610
+ l.pendingValues.includes($) ? l.pendingValues = l.pendingValues.filter((S) => S !== $) : l.pendingValues = [...l.pendingValues, $], n(c, d, l);
1611
+ });
1612
+ }));
1613
+ };
1614
+ t.forEach((c) => {
1615
+ var w, k, x, $;
1616
+ const d = f.get(c.filterId), l = e.querySelector(`.ds-fp-item[data-filter-id="${c.filterId}"]`);
1617
+ if (!l) return;
1618
+ (w = l.querySelector(".ds-fp-op-select")) == null || w.addEventListener("change", (y) => {
1619
+ d.operator = y.target.value;
1620
+ });
1621
+ const T = l.querySelector(".ds-fp-ms-search");
1622
+ T && (T.addEventListener("input", () => {
1623
+ d.search = T.value, n(c.filterId, l, d);
1624
+ }), (k = l.querySelector(".ds-fp-search-caret")) == null || k.addEventListener("click", () => {
1625
+ const y = l.querySelector(".ds-fp-opts");
1626
+ y && (y.style.display = y.style.display === "none" ? "" : "none");
1627
+ })), l.querySelectorAll(".ds-fp-chip-x").forEach((y) => {
1628
+ y.addEventListener("click", (S) => {
1629
+ S.stopPropagation(), d.pendingValues = d.pendingValues.filter((C) => {
1630
+ var P;
1631
+ return C !== ((P = y.dataset.val) != null ? P : "");
1632
+ }), n(c.filterId, l, d);
1633
+ });
1634
+ }), l.querySelectorAll(".ds-fp-opt").forEach((y) => {
1635
+ y.addEventListener("click", () => {
1636
+ var C;
1637
+ const S = (C = y.dataset.val) != null ? C : "";
1638
+ d.pendingValues.includes(S) ? d.pendingValues = d.pendingValues.filter((P) => P !== S) : d.pendingValues = [...d.pendingValues, S], n(c.filterId, l, d);
1639
+ });
1640
+ }), (x = l.querySelector(`.ds-fp-apply[data-apply-id="${c.filterId}"]`)) == null || x.addEventListener("click", () => {
1641
+ var S, C, P;
1642
+ const y = d.pendingValues.length > 1 ? [...d.pendingValues] : (S = d.pendingValues[0]) != null ? S : "";
1643
+ !y || Array.isArray(y) && !y.length ? delete this.activeLocalFilters[c.filterId] : this.activeLocalFilters[c.filterId] = { value: y, operator: d.operator || "=" }, (P = (C = this.opts).onFilterChange) == null || P.call(C, this.activeFilters), this.opts.onTabSwitch(this.activePageId, this.activeTabId), e.style.display = "none";
1644
+ }), !(($ = c.options) != null && $.length) && this.opts.onFetchFilterOptions && this.opts.onFetchFilterOptions(c.filterId).then((y) => {
1645
+ l.querySelector(`.ds-fp-opts[data-filter-id="${c.filterId}"]`) && (c.options = y.map((C) => typeof C == "string" ? C : C.value), n(c.filterId, l, d));
1646
+ }).catch(() => {
1647
+ });
1648
+ });
1649
+ }
1070
1650
  // ── Spinner ───────────────────────────────────────────────────────────────
1071
1651
  showTabSpinner() {
1072
1652
  const e = this.root.querySelector("#ds-tab-loading"), t = this.root.querySelector("#ds-grid");
@@ -1084,8 +1664,8 @@ const H = class H {
1084
1664
  * (so it can support CSS gradients via `background` shorthand).
1085
1665
  */
1086
1666
  scToInlineStyle(e) {
1087
- const t = /* @__PURE__ */ new Set(["backgroundColor"]), s = (i) => i.replace(/([A-Z])/g, "-$1").toLowerCase();
1088
- return Object.entries(e).filter(([i, r]) => !t.has(i) && r != null && r !== "").map(([i, r]) => `${s(i)}:${r}`).join(";");
1667
+ const t = /* @__PURE__ */ new Set(["backgroundColor"]), s = (r) => r.replace(/([A-Z])/g, "-$1").toLowerCase();
1668
+ return Object.entries(e).filter(([r, o]) => !t.has(r) && o != null && o !== "").map(([r, o]) => `${s(r)}:${o}`).join(";");
1089
1669
  }
1090
1670
  /**
1091
1671
  * Parse a Tailwind utility class string (e.g. "bg-violet-100 text-violet-600")
@@ -1117,19 +1697,39 @@ const H = class H {
1117
1697
  t.text = "#000000";
1118
1698
  continue;
1119
1699
  }
1120
- const i = /^(bg|text)-([a-z]+)-(\d+)$/.exec(s);
1121
- if (!i) continue;
1122
- const r = H.TW_PALETTE[i[2]], a = r == null ? void 0 : r[Number(i[3])];
1123
- a && (i[1] === "bg" && (t.bg = a), i[1] === "text" && (t.text = a));
1700
+ const r = /^(bg|text)-([a-z]+)-(\d+)$/.exec(s);
1701
+ if (!r) continue;
1702
+ const o = N.TW_PALETTE[r[2]], i = o == null ? void 0 : o[Number(r[3])];
1703
+ i && (r[1] === "bg" && (t.bg = i), r[1] === "text" && (t.text = i));
1124
1704
  }
1125
1705
  return t;
1126
1706
  }
1707
+ getTooltipEl() {
1708
+ if (!this.tooltipEl) {
1709
+ const e = document.createElement("div");
1710
+ e.className = "ds-global-tooltip", e.style.display = "none", document.body.appendChild(e), this.tooltipEl = e;
1711
+ }
1712
+ return this.tooltipEl;
1713
+ }
1714
+ getComponentDescription(e) {
1715
+ var t, s, r, o;
1716
+ if ((t = e.properties) != null && t.description) return String(e.properties.description);
1717
+ try {
1718
+ const i = (r = (s = e.properties) == null ? void 0 : s.data) == null ? void 0 : r.dataContent;
1719
+ if (i) {
1720
+ const a = typeof i == "string" ? JSON.parse(i) : i;
1721
+ return (a == null ? void 0 : a.description) || ((o = a == null ? void 0 : a.config) == null ? void 0 : o.description) || "";
1722
+ }
1723
+ } catch (i) {
1724
+ }
1725
+ return "";
1726
+ }
1127
1727
  getTabsForPage(e) {
1128
1728
  var t, s;
1129
- return (s = (t = this.renderData.dashboard.pages.find((i) => i.pageId === e)) == null ? void 0 : t.tabs) != null ? s : [];
1729
+ return (s = (t = this.renderData.dashboard.pages.find((r) => r.pageId === e)) == null ? void 0 : t.tabs) != null ? s : [];
1130
1730
  }
1131
1731
  };
1132
- H.TW_PALETTE = {
1732
+ N.TW_PALETTE = {
1133
1733
  slate: { 50: "#f8fafc", 100: "#f1f5f9", 200: "#e2e8f0", 300: "#cbd5e1", 400: "#94a3b8", 500: "#64748b", 600: "#475569", 700: "#334155", 800: "#1e293b", 900: "#0f172a" },
1134
1734
  gray: { 50: "#f9fafb", 100: "#f3f4f6", 200: "#e5e7eb", 300: "#d1d5db", 400: "#9ca3af", 500: "#6b7280", 600: "#4b5563", 700: "#374151", 800: "#1f2937", 900: "#111827" },
1135
1735
  zinc: { 50: "#fafafa", 100: "#f4f4f5", 200: "#e4e4e7", 300: "#d4d4d8", 400: "#a1a1aa", 500: "#71717a", 600: "#52525b", 700: "#3f3f46", 800: "#27272a", 900: "#18181b" },
@@ -1153,8 +1753,8 @@ H.TW_PALETTE = {
1153
1753
  pink: { 50: "#fdf2f8", 100: "#fce7f3", 200: "#fbcfe8", 300: "#f9a8d4", 400: "#f472b6", 500: "#ec4899", 600: "#db2777", 700: "#be185d", 800: "#9d174d", 900: "#831843" },
1154
1754
  rose: { 50: "#fff1f2", 100: "#ffe4e6", 200: "#fecdd3", 300: "#fda4af", 400: "#fb7185", 500: "#f43f5e", 600: "#e11d48", 700: "#be123c", 800: "#9f1239", 900: "#881337" }
1155
1755
  };
1156
- let D = H;
1157
- class he {
1756
+ let j = N;
1757
+ class pe {
1158
1758
  constructor(e, t) {
1159
1759
  this.chartRenderer = null, this.tableRenderer = new le(), this.cardRenderer = new ce(), this.root = e, this.theme = t;
1160
1760
  }
@@ -1167,8 +1767,8 @@ class he {
1167
1767
  </div>`;
1168
1768
  return;
1169
1769
  }
1170
- const i = e.data[s.id] || [];
1171
- this.renderComponent(s, i), t == null || t();
1770
+ const r = e.data[s.id] || [];
1771
+ this.renderComponent(s, r), t == null || t();
1172
1772
  }
1173
1773
  update(e) {
1174
1774
  const t = e.components[0];
@@ -1215,12 +1815,12 @@ class he {
1215
1815
  }
1216
1816
  }
1217
1817
  }
1218
- let oe = !1;
1219
- function q() {
1220
- if (!(oe || typeof document == "undefined")) {
1221
- oe = !0;
1818
+ let ae = !1;
1819
+ function K() {
1820
+ if (!(ae || typeof document == "undefined")) {
1821
+ ae = !0;
1222
1822
  try {
1223
- const w = `/* ─────────────────────────────────────────────────────────────────────────────
1823
+ const A = `/* ─────────────────────────────────────────────────────────────────────────────
1224
1824
  Deepspot SDK — Base Embed Styles
1225
1825
  Injected once into the host page's <head> by the SDK.
1226
1826
  All selectors are scoped under .ds-embed-* to avoid leaking into host styles.
@@ -1450,7 +2050,25 @@ function q() {
1450
2050
 
1451
2051
  .ds-component-wrapper {
1452
2052
  position: absolute;
1453
- overflow: hidden;
2053
+ /* overflow intentionally not set — individual card components handle their own clipping,
2054
+ and tooltip bubbles must be able to overflow the wrapper boundary */
2055
+ }
2056
+
2057
+ /* ── Description tooltip — fixed on body, positioned via JS, never clips content ── */
2058
+ .ds-global-tooltip {
2059
+ position: fixed;
2060
+ z-index: 9999;
2061
+ background: rgba(15, 23, 42, 0.92);
2062
+ color: #f1f5f9;
2063
+ font-size: 12px;
2064
+ line-height: 1.6;
2065
+ padding: 8px 14px;
2066
+ border-radius: 8px;
2067
+ white-space: normal;
2068
+ pointer-events: none;
2069
+ backdrop-filter: blur(4px);
2070
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
2071
+ max-width: 600px;
1454
2072
  }
1455
2073
 
1456
2074
  /* ── Chart component ───────────────────────────────────────────────────────── */
@@ -1964,121 +2582,1788 @@ function q() {
1964
2582
  border-bottom-color: #334155;
1965
2583
  }
1966
2584
 
1967
- /* ── v2: Inline filter (selection-filter placed in-grid) ───────────────────── */
2585
+ /* ── Filter panel (publish-page style: toggle + collapsible items + Apply) ─── */
1968
2586
 
1969
- .ds-inline-filter {
1970
- width: 100%;
1971
- height: 100%;
1972
- display: flex;
1973
- flex-direction: column;
1974
- justify-content: center;
1975
- gap: 4px;
1976
- padding: 6px 10px;
2587
+ .ds-fp {
2588
+ border-bottom: 1px solid #e5e7eb;
2589
+ background: inherit;
1977
2590
  }
1978
2591
 
1979
- .ds-inline-filter-select,
1980
- .ds-inline-filter-input {
1981
- width: 100%;
1982
- height: 34px;
1983
- padding: 0 10px;
1984
- border: 1px solid #d1d5db;
1985
- border-radius: 6px;
1986
- font-size: 13px;
1987
- background: #ffffff;
1988
- color: #111827;
1989
- cursor: pointer;
1990
- outline: none;
1991
- transition: border-color 0.15s, box-shadow 0.15s;
2592
+ .ds-theme-dark .ds-fp {
2593
+ border-bottom-color: #1e293b;
1992
2594
  }
1993
2595
 
1994
- .ds-inline-filter-select:focus,
1995
- .ds-inline-filter-input:focus {
1996
- border-color: #6366f1;
1997
- box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
2596
+ /* ── Toggle bar ────────────────────────────────────────────────────────────── */
2597
+ .ds-fp-bar {
2598
+ display: flex;
2599
+ align-items: center;
2600
+ gap: 12px;
2601
+ padding: 8px 16px;
1998
2602
  }
1999
2603
 
2000
- .ds-theme-dark .ds-inline-filter-select,
2001
- .ds-theme-dark .ds-inline-filter-input {
2002
- background: #1e293b;
2003
- border-color: #334155;
2004
- color: #f1f5f9;
2604
+ /* ── Panel header ──────────────────────────────────────────────────────────── */
2605
+ .ds-fp-panel-hd {
2606
+ display: flex;
2607
+ align-items: center;
2608
+ justify-content: space-between;
2609
+ padding: 14px 16px 12px;
2610
+ border-bottom: 1px solid #e5e7eb;
2005
2611
  }
2006
2612
 
2007
- /* ── Export toolbar ────────────────────────────────────────────────────────── */
2008
- .ds-toolbar {
2009
- display: flex;
2010
- justify-content: flex-end;
2011
- padding: 8px 16px 0;
2012
- gap: 8px;
2613
+ .ds-theme-dark .ds-fp-panel-hd {
2614
+ border-bottom-color: #334155;
2013
2615
  }
2014
2616
 
2015
- .ds-toolbar-btn {
2016
- height: 30px;
2017
- padding: 0 12px;
2018
- border: 1px solid #d1d5db;
2019
- border-radius: 6px;
2020
- font-size: 12px;
2021
- font-weight: 500;
2022
- background: #ffffff;
2023
- color: #374151;
2024
- cursor: pointer;
2617
+ .ds-fp-panel-title {
2025
2618
  display: flex;
2026
2619
  align-items: center;
2027
- gap: 5px;
2028
- transition: background 0.15s, border-color 0.15s;
2620
+ gap: 8px;
2621
+ font-size: 14px;
2622
+ font-weight: 600;
2623
+ color: #111827;
2029
2624
  }
2030
2625
 
2031
- .ds-toolbar-btn:hover {
2032
- background: #f9fafb;
2033
- border-color: #9ca3af;
2626
+ .ds-theme-dark .ds-fp-panel-title {
2627
+ color: #f1f5f9;
2034
2628
  }
2035
2629
 
2036
- .ds-theme-dark .ds-toolbar-btn {
2037
- background: #1e293b;
2038
- border-color: #334155;
2039
- color: #e2e8f0;
2630
+ .ds-fp-panel-title svg {
2631
+ width: 16px;
2632
+ height: 16px;
2633
+ color: #6b7280;
2634
+ flex-shrink: 0;
2040
2635
  }
2041
2636
 
2042
- .ds-theme-dark .ds-toolbar-btn:hover {
2043
- background: #0f172a;
2044
- }
2045
- `, e = document.createElement("style");
2046
- e.id = "deepspot-sdk-styles", e.textContent = w, document.head.appendChild(e);
2047
- } catch (w) {
2048
- }
2049
- }
2050
- }
2051
- function de(w) {
2052
- if (typeof w == "string") {
2053
- const e = document.querySelector(w);
2054
- if (!e) throw new Error(`Deepspot SDK: container "${w}" not found in DOM`);
2055
- return e;
2056
- }
2057
- return w;
2058
- }
2059
- class ge {
2637
+ .ds-fp-close-btn {
2638
+ width: 28px;
2639
+ height: 28px;
2640
+ display: flex;
2641
+ align-items: center;
2642
+ justify-content: center;
2643
+ border: 1px solid #e5e7eb;
2644
+ border-radius: 6px;
2645
+ background: transparent;
2646
+ color: #6b7280;
2647
+ cursor: pointer;
2648
+ padding: 0;
2649
+ transition: border-color 0.15s, color 0.15s, background 0.15s;
2650
+ flex-shrink: 0;
2651
+ }
2652
+
2653
+ .ds-fp-close-btn svg {
2654
+ width: 14px;
2655
+ height: 14px;
2656
+ }
2657
+
2658
+ .ds-fp-close-btn:hover {
2659
+ border-color: #6366f1;
2660
+ color: #6366f1;
2661
+ background: #eef2ff;
2662
+ }
2663
+
2664
+ .ds-theme-dark .ds-fp-close-btn {
2665
+ border-color: #334155;
2666
+ color: #94a3b8;
2667
+ }
2668
+
2669
+ /* ── Reset All section (inside panel) ─────────────────────────────────────── */
2670
+ .ds-fp-reset-section {
2671
+ padding: 10px 16px;
2672
+ border-bottom: 1px solid #f3f4f6;
2673
+ }
2674
+
2675
+ .ds-theme-dark .ds-fp-reset-section {
2676
+ border-bottom-color: #1e293b;
2677
+ }
2678
+
2679
+ .ds-fp-reset-all {
2680
+ display: inline-flex;
2681
+ align-items: center;
2682
+ gap: 6px;
2683
+ border: none;
2684
+ background: transparent;
2685
+ color: #6366f1;
2686
+ font-size: 13px;
2687
+ font-weight: 500;
2688
+ cursor: pointer;
2689
+ padding: 0;
2690
+ transition: color 0.15s;
2691
+ }
2692
+
2693
+ .ds-fp-reset-all svg {
2694
+ width: 13px;
2695
+ height: 13px;
2696
+ }
2697
+
2698
+ .ds-fp-reset-all:hover {
2699
+ color: #4338ca;
2700
+ }
2701
+
2702
+ .ds-theme-dark .ds-fp-reset-all {
2703
+ color: #818cf8;
2704
+ }
2705
+
2706
+ .ds-fp-toggle {
2707
+ display: flex;
2708
+ align-items: center;
2709
+ gap: 6px;
2710
+ padding: 6px 12px;
2711
+ border: 1px solid #d1d5db;
2712
+ border-radius: 8px;
2713
+ background: #ffffff;
2714
+ color: #374151;
2715
+ font-size: 13px;
2716
+ font-weight: 500;
2717
+ cursor: pointer;
2718
+ transition: border-color 0.15s, background 0.15s;
2719
+ }
2720
+
2721
+ .ds-fp-toggle:hover {
2722
+ border-color: #6366f1;
2723
+ background: #f5f3ff;
2724
+ }
2725
+
2726
+ .ds-theme-dark .ds-fp-toggle {
2727
+ background: #1e293b;
2728
+ border-color: #334155;
2729
+ color: #e2e8f0;
2730
+ }
2731
+
2732
+ .ds-fp-funnel {
2733
+ width: 13px;
2734
+ height: 13px;
2735
+ flex-shrink: 0;
2736
+ }
2737
+
2738
+ .ds-fp-badge {
2739
+ display: inline-flex;
2740
+ align-items: center;
2741
+ justify-content: center;
2742
+ min-width: 18px;
2743
+ height: 18px;
2744
+ padding: 0 5px;
2745
+ border-radius: 9px;
2746
+ background: #6366f1;
2747
+ color: #fff;
2748
+ font-size: 11px;
2749
+ font-weight: 700;
2750
+ line-height: 1;
2751
+ }
2752
+
2753
+ .ds-fp-reset-all {
2754
+ border: none;
2755
+ background: transparent;
2756
+ color: #6366f1;
2757
+ font-size: 12px;
2758
+ font-weight: 500;
2759
+ cursor: pointer;
2760
+ padding: 0;
2761
+ text-decoration: underline;
2762
+ transition: color 0.15s;
2763
+ }
2764
+
2765
+ .ds-fp-reset-all:hover {
2766
+ color: #4338ca;
2767
+ }
2768
+
2769
+ .ds-theme-dark .ds-fp-reset-all {
2770
+ color: #818cf8;
2771
+ }
2772
+
2773
+ /* ── Expandable panel ──────────────────────────────────────────────────────── */
2774
+ .ds-fp-panel {
2775
+ padding: 0 16px 10px;
2776
+ }
2777
+
2778
+ .ds-fp-items {
2779
+ display: flex;
2780
+ flex-direction: column;
2781
+ gap: 4px;
2782
+ }
2783
+
2784
+ /* ── Filter item ───────────────────────────────────────────────────────────── */
2785
+ .ds-fp-item {
2786
+ border: 1px solid #e5e7eb;
2787
+ border-radius: 8px;
2788
+ overflow: hidden;
2789
+ background: #fafafa;
2790
+ }
2791
+
2792
+ .ds-theme-dark .ds-fp-item {
2793
+ border-color: #334155;
2794
+ background: #1e293b;
2795
+ }
2796
+
2797
+ .ds-fp-item-hd {
2798
+ display: flex;
2799
+ align-items: center;
2800
+ gap: 8px;
2801
+ padding: 9px 12px;
2802
+ cursor: pointer;
2803
+ user-select: none;
2804
+ transition: background 0.12s;
2805
+ }
2806
+
2807
+ .ds-fp-item-hd:hover {
2808
+ background: #f0f0ff;
2809
+ }
2810
+
2811
+ .ds-theme-dark .ds-fp-item-hd:hover {
2812
+ background: #0f172a;
2813
+ }
2814
+
2815
+ .ds-fp-item-name {
2816
+ flex: 1;
2817
+ font-size: 13px;
2818
+ font-weight: 600;
2819
+ color: #374151;
2820
+ }
2821
+
2822
+ .ds-theme-dark .ds-fp-item-name {
2823
+ color: #e2e8f0;
2824
+ }
2825
+
2826
+ .ds-fp-val-badge {
2827
+ font-size: 11px;
2828
+ font-weight: 600;
2829
+ padding: 2px 7px;
2830
+ border-radius: 10px;
2831
+ background: #e0e7ff;
2832
+ color: #4338ca;
2833
+ white-space: nowrap;
2834
+ max-width: 120px;
2835
+ overflow: hidden;
2836
+ text-overflow: ellipsis;
2837
+ }
2838
+
2839
+ .ds-theme-dark .ds-fp-val-badge {
2840
+ background: #312e81;
2841
+ color: #a5b4fc;
2842
+ }
2843
+
2844
+ .ds-fp-item-clear {
2845
+ border: none;
2846
+ background: transparent;
2847
+ color: #9ca3af;
2848
+ cursor: pointer;
2849
+ padding: 2px;
2850
+ line-height: 1;
2851
+ flex-shrink: 0;
2852
+ transition: color 0.12s;
2853
+ display: flex;
2854
+ align-items: center;
2855
+ }
2856
+
2857
+ .ds-fp-item-clear svg {
2858
+ width: 13px;
2859
+ height: 13px;
2860
+ }
2861
+
2862
+ .ds-fp-item-clear:hover {
2863
+ color: #6366f1;
2864
+ }
2865
+
2866
+ .ds-fp-chevron {
2867
+ width: 14px;
2868
+ height: 14px;
2869
+ color: #9ca3af;
2870
+ flex-shrink: 0;
2871
+ transition: transform 0.2s ease;
2872
+ }
2873
+
2874
+ /* Indigo border + highlight when expanded */
2875
+ .ds-fp-item-expanded,
2876
+ .ds-fp-item:has(.ds-fp-item-body) {
2877
+ border-color: #6366f1;
2878
+ background: #fafafe;
2879
+ }
2880
+
2881
+ .ds-theme-dark .ds-fp-item-expanded,
2882
+ .ds-theme-dark .ds-fp-item:has(.ds-fp-item-body) {
2883
+ border-color: #6366f1;
2884
+ background: #1e1e3a;
2885
+ }
2886
+
2887
+ /* ── Item body (expanded content) ──────────────────────────────────────────── */
2888
+ .ds-fp-item-body {
2889
+ padding: 4px 12px 12px;
2890
+ display: flex;
2891
+ flex-direction: column;
2892
+ gap: 8px;
2893
+ border-top: 1px solid #e5e7eb;
2894
+ }
2895
+
2896
+ .ds-theme-dark .ds-fp-item-body {
2897
+ border-top-color: #334155;
2898
+ }
2899
+
2900
+ /* ── Operator row ──────────────────────────────────────────────────────────── */
2901
+ .ds-fp-op-row {
2902
+ display: flex;
2903
+ align-items: center;
2904
+ gap: 8px;
2905
+ }
2906
+
2907
+ .ds-fp-op-lbl {
2908
+ font-size: 11px;
2909
+ font-weight: 600;
2910
+ text-transform: uppercase;
2911
+ letter-spacing: 0.04em;
2912
+ color: #9ca3af;
2913
+ white-space: nowrap;
2914
+ flex-shrink: 0;
2915
+ }
2916
+
2917
+ .ds-fp-op-select {
2918
+ flex: 1;
2919
+ height: 30px;
2920
+ padding: 0 8px;
2921
+ border: 1px solid #d1d5db;
2922
+ border-radius: 6px;
2923
+ font-size: 12px;
2924
+ background: #fff;
2925
+ color: #374151;
2926
+ cursor: pointer;
2927
+ outline: none;
2928
+ transition: border-color 0.15s;
2929
+ }
2930
+
2931
+ .ds-fp-op-select:focus {
2932
+ border-color: #6366f1;
2933
+ box-shadow: 0 0 0 2px rgba(99,102,241,0.15);
2934
+ }
2935
+
2936
+ .ds-theme-dark .ds-fp-op-select {
2937
+ background: #0f172a;
2938
+ border-color: #334155;
2939
+ color: #e2e8f0;
2940
+ }
2941
+
2942
+ /* ── Search wrap (input + caret button) ────────────────────────────────────── */
2943
+ .ds-fp-search-wrap {
2944
+ display: flex;
2945
+ align-items: center;
2946
+ border: 1px solid #6366f1;
2947
+ border-radius: 8px;
2948
+ background: #fff;
2949
+ overflow: hidden;
2950
+ transition: box-shadow 0.15s;
2951
+ }
2952
+
2953
+ .ds-fp-search-wrap:focus-within {
2954
+ box-shadow: 0 0 0 3px rgba(99,102,241,0.15);
2955
+ }
2956
+
2957
+ .ds-theme-dark .ds-fp-search-wrap {
2958
+ background: #0f172a;
2959
+ border-color: #6366f1;
2960
+ }
2961
+
2962
+ .ds-fp-ms-search {
2963
+ flex: 1;
2964
+ height: 36px;
2965
+ padding: 0 10px;
2966
+ border: none;
2967
+ outline: none;
2968
+ font-size: 13px;
2969
+ background: transparent;
2970
+ color: #374151;
2971
+ }
2972
+
2973
+ .ds-theme-dark .ds-fp-ms-search {
2974
+ color: #e2e8f0;
2975
+ }
2976
+
2977
+ .ds-fp-ms-search::placeholder {
2978
+ color: #9ca3af;
2979
+ }
2980
+
2981
+ .ds-fp-search-caret {
2982
+ width: 32px;
2983
+ height: 36px;
2984
+ display: flex;
2985
+ align-items: center;
2986
+ justify-content: center;
2987
+ border: none;
2988
+ border-left: 1px solid #e5e7eb;
2989
+ background: transparent;
2990
+ color: #6b7280;
2991
+ cursor: pointer;
2992
+ padding: 0;
2993
+ flex-shrink: 0;
2994
+ transition: color 0.12s, background 0.12s;
2995
+ }
2996
+
2997
+ .ds-fp-search-caret:hover {
2998
+ color: #6366f1;
2999
+ background: #eef2ff;
3000
+ }
3001
+
3002
+ .ds-theme-dark .ds-fp-search-caret {
3003
+ border-left-color: #334155;
3004
+ }
3005
+
3006
+ /* ── Multi-select container ────────────────────────────────────────────────── */
3007
+ .ds-fp-ms {
3008
+ border: 1px solid #e5e7eb;
3009
+ border-radius: 8px;
3010
+ overflow: hidden;
3011
+ }
3012
+
3013
+ .ds-theme-dark .ds-fp-ms {
3014
+ border-color: #334155;
3015
+ }
3016
+
3017
+ /* Chips row (shown above search when values selected) */
3018
+ .ds-fp-chips-row {
3019
+ display: flex;
3020
+ flex-wrap: wrap;
3021
+ align-items: center;
3022
+ gap: 4px;
3023
+ padding: 6px 0 4px;
3024
+ }
3025
+
3026
+ .ds-fp-chip {
3027
+ display: inline-flex;
3028
+ align-items: center;
3029
+ gap: 4px;
3030
+ padding: 2px 8px 2px 8px;
3031
+ border-radius: 12px;
3032
+ background: #e0e7ff;
3033
+ color: #4338ca;
3034
+ font-size: 11px;
3035
+ font-weight: 600;
3036
+ white-space: nowrap;
3037
+ }
3038
+
3039
+ .ds-theme-dark .ds-fp-chip {
3040
+ background: #312e81;
3041
+ color: #a5b4fc;
3042
+ }
3043
+
3044
+ .ds-fp-chip-x {
3045
+ border: none;
3046
+ background: transparent;
3047
+ color: inherit;
3048
+ font-size: 10px;
3049
+ cursor: pointer;
3050
+ padding: 0;
3051
+ line-height: 1;
3052
+ opacity: 0.7;
3053
+ transition: opacity 0.1s;
3054
+ }
3055
+
3056
+ .ds-fp-chip-x:hover {
3057
+ opacity: 1;
3058
+ }
3059
+
3060
+ /* ds-fp-ms-search styles live in .ds-fp-search-wrap above */
3061
+
3062
+ /* Options list */
3063
+ .ds-fp-opts {
3064
+ max-height: 180px;
3065
+ overflow-y: auto;
3066
+ }
3067
+
3068
+ .ds-fp-opt {
3069
+ display: flex;
3070
+ align-items: center;
3071
+ gap: 8px;
3072
+ padding: 7px 10px;
3073
+ cursor: pointer;
3074
+ transition: background 0.1s;
3075
+ user-select: none;
3076
+ }
3077
+
3078
+ .ds-fp-opt:hover {
3079
+ background: #f0f0ff;
3080
+ }
3081
+
3082
+ .ds-theme-dark .ds-fp-opt:hover {
3083
+ background: #1e293b;
3084
+ }
3085
+
3086
+ .ds-fp-opt-on {
3087
+ background: #eef2ff;
3088
+ }
3089
+
3090
+ .ds-theme-dark .ds-fp-opt-on {
3091
+ background: #1e293b;
3092
+ }
3093
+
3094
+ /* Custom checkbox */
3095
+ .ds-fp-cb {
3096
+ width: 16px;
3097
+ height: 16px;
3098
+ border: 2px solid #d1d5db;
3099
+ border-radius: 4px;
3100
+ flex-shrink: 0;
3101
+ display: flex;
3102
+ align-items: center;
3103
+ justify-content: center;
3104
+ transition: border-color 0.1s, background 0.1s;
3105
+ }
3106
+
3107
+ .ds-fp-cb-on {
3108
+ border-color: #6366f1;
3109
+ background: #6366f1;
3110
+ }
3111
+
3112
+ .ds-fp-cb-on svg {
3113
+ width: 10px;
3114
+ height: 10px;
3115
+ stroke: #fff;
3116
+ }
3117
+
3118
+ .ds-fp-opt-lbl {
3119
+ font-size: 12px;
3120
+ color: #374151;
3121
+ flex: 1;
3122
+ overflow: hidden;
3123
+ text-overflow: ellipsis;
3124
+ white-space: nowrap;
3125
+ }
3126
+
3127
+ .ds-theme-dark .ds-fp-opt-lbl {
3128
+ color: #cbd5e1;
3129
+ }
3130
+
3131
+ .ds-fp-opts-msg {
3132
+ display: flex;
3133
+ align-items: center;
3134
+ justify-content: center;
3135
+ gap: 8px;
3136
+ padding: 14px 12px;
3137
+ font-size: 12px;
3138
+ color: #9ca3af;
3139
+ }
3140
+
3141
+ /* ── Apply Filter button ───────────────────────────────────────────────────── */
3142
+ .ds-fp-apply {
3143
+ width: 100%;
3144
+ padding: 9px 16px;
3145
+ border: none;
3146
+ border-radius: 8px;
3147
+ background: linear-gradient(135deg, #16a34a, #15803d);
3148
+ color: #fff;
3149
+ font-size: 13px;
3150
+ font-weight: 600;
3151
+ cursor: pointer;
3152
+ transition: opacity 0.15s, transform 0.1s;
3153
+ letter-spacing: 0.01em;
3154
+ }
3155
+
3156
+ .ds-fp-apply:hover {
3157
+ opacity: 0.9;
3158
+ }
3159
+
3160
+ .ds-fp-apply:active {
3161
+ transform: scale(0.98);
3162
+ }
3163
+
3164
+ /* ── Text / date inputs inside filter body ─────────────────────────────────── */
3165
+ .ds-fp-text-val {
3166
+ width: 100%;
3167
+ height: 34px;
3168
+ padding: 0 10px;
3169
+ border: 1px solid #d1d5db;
3170
+ border-radius: 6px;
3171
+ font-size: 13px;
3172
+ background: #fff;
3173
+ color: #374151;
3174
+ outline: none;
3175
+ transition: border-color 0.15s;
3176
+ }
3177
+
3178
+ .ds-fp-text-val:focus {
3179
+ border-color: #6366f1;
3180
+ box-shadow: 0 0 0 2px rgba(99,102,241,0.15);
3181
+ }
3182
+
3183
+ .ds-theme-dark .ds-fp-text-val {
3184
+ background: #0f172a;
3185
+ border-color: #334155;
3186
+ color: #e2e8f0;
3187
+ }
3188
+
3189
+ .ds-fp-date-range {
3190
+ display: flex;
3191
+ align-items: center;
3192
+ gap: 6px;
3193
+ }
3194
+
3195
+ .ds-fp-date {
3196
+ flex: 1;
3197
+ height: 34px;
3198
+ padding: 0 8px;
3199
+ border: 1px solid #d1d5db;
3200
+ border-radius: 6px;
3201
+ font-size: 12px;
3202
+ background: #fff;
3203
+ color: #374151;
3204
+ outline: none;
3205
+ transition: border-color 0.15s;
3206
+ }
3207
+
3208
+ .ds-fp-date:focus {
3209
+ border-color: #6366f1;
3210
+ }
3211
+
3212
+ .ds-fp-date-sep {
3213
+ color: #9ca3af;
3214
+ font-size: 12px;
3215
+ }
3216
+
3217
+ .ds-theme-dark .ds-fp-date {
3218
+ background: #0f172a;
3219
+ border-color: #334155;
3220
+ color: #e2e8f0;
3221
+ }
3222
+
3223
+ /* ── Component-level inline filter button (funnel icon on chart card) ──────── */
3224
+ .ds-comp-filter-btn {
3225
+ position: absolute;
3226
+ top: 6px;
3227
+ right: 6px;
3228
+ z-index: 10;
3229
+ width: 28px;
3230
+ height: 28px;
3231
+ display: flex;
3232
+ align-items: center;
3233
+ justify-content: center;
3234
+ border: 1px solid #e5e7eb;
3235
+ border-radius: 6px;
3236
+ background: rgba(255,255,255,0.9);
3237
+ color: #6b7280;
3238
+ cursor: pointer;
3239
+ padding: 0;
3240
+ transition: border-color 0.15s, color 0.15s, background 0.15s;
3241
+ backdrop-filter: blur(4px);
3242
+ }
3243
+
3244
+ .ds-comp-filter-btn:hover {
3245
+ border-color: #6366f1;
3246
+ color: #6366f1;
3247
+ background: #fff;
3248
+ }
3249
+
3250
+ .ds-comp-filter-btn-active {
3251
+ border-color: #6366f1;
3252
+ color: #6366f1;
3253
+ background: #eef2ff;
3254
+ }
3255
+
3256
+ .ds-comp-filter-btn svg {
3257
+ width: 13px;
3258
+ height: 13px;
3259
+ }
3260
+
3261
+ .ds-theme-dark .ds-comp-filter-btn {
3262
+ background: rgba(15,23,42,0.85);
3263
+ border-color: #334155;
3264
+ color: #94a3b8;
3265
+ }
3266
+
3267
+ .ds-theme-dark .ds-comp-filter-btn-active {
3268
+ border-color: #6366f1;
3269
+ color: #818cf8;
3270
+ background: #1e293b;
3271
+ }
3272
+
3273
+ /* ── Inline filter popover ─────────────────────────────────────────────────── */
3274
+ .ds-comp-filter-pop {
3275
+ position: absolute;
3276
+ top: 38px;
3277
+ right: 6px;
3278
+ z-index: 9999;
3279
+ }
3280
+
3281
+ /* Visual shell for the inline popover — same look as global filter panel */
3282
+ .ds-fp-panel-pop {
3283
+ width: 300px;
3284
+ max-height: 440px;
3285
+ overflow-y: auto;
3286
+ background: #fff;
3287
+ border: 1px solid #e5e7eb;
3288
+ border-radius: 10px;
3289
+ box-shadow: 0 8px 24px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.06);
3290
+ }
3291
+
3292
+ .ds-theme-dark .ds-fp-panel-pop {
3293
+ background: #1e293b;
3294
+ border-color: #334155;
3295
+ box-shadow: 0 8px 24px rgba(0,0,0,0.4);
3296
+ }
3297
+
3298
+ .ds-cpop-inner {
3299
+ display: flex;
3300
+ flex-direction: column;
3301
+ gap: 6px;
3302
+ padding: 10px;
3303
+ }
3304
+
3305
+ .ds-cpop-title {
3306
+ font-size: 11px;
3307
+ font-weight: 700;
3308
+ text-transform: uppercase;
3309
+ letter-spacing: 0.05em;
3310
+ color: #6b7280;
3311
+ padding-bottom: 4px;
3312
+ border-bottom: 1px solid #f3f4f6;
3313
+ }
3314
+
3315
+ .ds-theme-dark .ds-cpop-title {
3316
+ color: #94a3b8;
3317
+ border-bottom-color: #334155;
3318
+ }
3319
+
3320
+ .ds-cpop-label {
3321
+ font-size: 12px;
3322
+ font-weight: 600;
3323
+ color: #374151;
3324
+ margin-bottom: 4px;
3325
+ }
3326
+
3327
+ .ds-theme-dark .ds-cpop-label {
3328
+ color: #e2e8f0;
3329
+ }
3330
+
3331
+ .ds-cpop-opts-wrap {
3332
+ border: 1px solid #e5e7eb;
3333
+ border-radius: 6px;
3334
+ overflow: hidden;
3335
+ margin-bottom: 6px;
3336
+ }
3337
+
3338
+ .ds-theme-dark .ds-cpop-opts-wrap {
3339
+ border-color: #334155;
3340
+ }
3341
+
3342
+ .ds-cpop-opts {
3343
+ max-height: 160px;
3344
+ overflow-y: auto;
3345
+ }
3346
+
3347
+ .ds-cpop-opt {
3348
+ display: flex;
3349
+ align-items: center;
3350
+ gap: 8px;
3351
+ padding: 6px 8px;
3352
+ cursor: pointer;
3353
+ font-size: 12px;
3354
+ color: #374151;
3355
+ transition: background 0.1s;
3356
+ user-select: none;
3357
+ }
3358
+
3359
+ .ds-cpop-opt:hover {
3360
+ background: #f0f0ff;
3361
+ }
3362
+
3363
+ .ds-theme-dark .ds-cpop-opt {
3364
+ color: #cbd5e1;
3365
+ }
3366
+
3367
+ .ds-theme-dark .ds-cpop-opt:hover {
3368
+ background: #0f172a;
3369
+ }
3370
+
3371
+ .ds-cpop-apply {
3372
+ font-size: 12px;
3373
+ padding: 7px 12px;
3374
+ }
3375
+
3376
+ /* ── Custom dropdown filter (matches publish page SelectFilter) ────────────── */
3377
+
3378
+ .ds-custom-dd {
3379
+ position: relative;
3380
+ min-width: 150px;
3381
+ }
3382
+
3383
+ .ds-dd-trigger {
3384
+ width: 100%;
3385
+ height: 34px;
3386
+ padding: 0 10px;
3387
+ display: flex;
3388
+ align-items: center;
3389
+ justify-content: space-between;
3390
+ gap: 6px;
3391
+ background: #ffffff;
3392
+ border: 1px solid #d1d5db;
3393
+ border-radius: 8px;
3394
+ font-size: 13px;
3395
+ color: #111827;
3396
+ cursor: pointer;
3397
+ text-align: left;
3398
+ transition: border-color 0.15s, box-shadow 0.15s;
3399
+ white-space: nowrap;
3400
+ }
3401
+
3402
+ .ds-dd-trigger:hover {
3403
+ border-color: #6366f1;
3404
+ }
3405
+
3406
+ .ds-dd-trigger:focus {
3407
+ outline: none;
3408
+ border-color: #6366f1;
3409
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
3410
+ }
3411
+
3412
+ .ds-theme-dark .ds-dd-trigger {
3413
+ background: #1e293b;
3414
+ border-color: #334155;
3415
+ color: #f1f5f9;
3416
+ }
3417
+
3418
+ .ds-dd-value {
3419
+ flex: 1;
3420
+ overflow: hidden;
3421
+ text-overflow: ellipsis;
3422
+ color: inherit;
3423
+ }
3424
+
3425
+ /* "All" placeholder style */
3426
+ .ds-dd-trigger:not(.ds-dd-has-value) .ds-dd-value {
3427
+ color: #9ca3af;
3428
+ }
3429
+
3430
+ .ds-dd-actions {
3431
+ display: flex;
3432
+ align-items: center;
3433
+ gap: 4px;
3434
+ flex-shrink: 0;
3435
+ }
3436
+
3437
+ .ds-dd-clear {
3438
+ font-size: 11px;
3439
+ color: #9ca3af;
3440
+ cursor: pointer;
3441
+ padding: 0 2px;
3442
+ line-height: 1;
3443
+ }
3444
+
3445
+ .ds-dd-clear:hover {
3446
+ color: #374151;
3447
+ }
3448
+
3449
+ .ds-theme-dark .ds-dd-clear:hover {
3450
+ color: #e2e8f0;
3451
+ }
3452
+
3453
+ .ds-dd-arrow {
3454
+ color: #9ca3af;
3455
+ transition: transform 0.18s ease;
3456
+ display: block;
3457
+ }
3458
+
3459
+ /* ── Dropdown panel ────────────────────────────────────────────────────────── */
3460
+
3461
+ .ds-dd-panel {
3462
+ position: absolute;
3463
+ top: calc(100% + 4px);
3464
+ left: 0;
3465
+ right: 0;
3466
+ min-width: 180px;
3467
+ background: #ffffff;
3468
+ border: 1px solid #e5e7eb;
3469
+ border-radius: 10px;
3470
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);
3471
+ z-index: 9999;
3472
+ overflow: hidden;
3473
+ }
3474
+
3475
+ .ds-theme-dark .ds-dd-panel {
3476
+ background: #1e293b;
3477
+ border-color: #334155;
3478
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
3479
+ }
3480
+
3481
+ /* Search row */
3482
+ .ds-dd-search-wrap {
3483
+ padding: 8px 8px 6px;
3484
+ border-bottom: 1px solid #f3f4f6;
3485
+ }
3486
+
3487
+ .ds-theme-dark .ds-dd-search-wrap {
3488
+ border-bottom-color: #334155;
3489
+ }
3490
+
3491
+ .ds-dd-search {
3492
+ width: 100%;
3493
+ height: 30px;
3494
+ padding: 0 10px;
3495
+ border: 1px solid #e5e7eb;
3496
+ border-radius: 6px;
3497
+ font-size: 12px;
3498
+ background: #f9fafb;
3499
+ color: #374151;
3500
+ outline: none;
3501
+ transition: border-color 0.15s, box-shadow 0.15s;
3502
+ }
3503
+
3504
+ .ds-dd-search:focus {
3505
+ border-color: #6366f1;
3506
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15);
3507
+ background: #fff;
3508
+ }
3509
+
3510
+ .ds-theme-dark .ds-dd-search {
3511
+ background: #0f172a;
3512
+ border-color: #334155;
3513
+ color: #e2e8f0;
3514
+ }
3515
+
3516
+ .ds-theme-dark .ds-dd-search:focus {
3517
+ background: #1e293b;
3518
+ }
3519
+
3520
+ /* Options list */
3521
+ .ds-dd-options {
3522
+ max-height: 200px;
3523
+ overflow-y: auto;
3524
+ }
3525
+
3526
+ .ds-dd-opt {
3527
+ width: 100%;
3528
+ padding: 8px 12px;
3529
+ display: flex;
3530
+ align-items: center;
3531
+ justify-content: space-between;
3532
+ gap: 8px;
3533
+ background: transparent;
3534
+ border: none;
3535
+ font-size: 13px;
3536
+ color: #374151;
3537
+ cursor: pointer;
3538
+ text-align: left;
3539
+ transition: background 0.1s;
3540
+ }
3541
+
3542
+ .ds-dd-opt:hover {
3543
+ background: #eef2ff;
3544
+ }
3545
+
3546
+ .ds-dd-opt-active {
3547
+ background: #eef2ff;
3548
+ color: #4338ca;
3549
+ font-weight: 600;
3550
+ }
3551
+
3552
+ .ds-dd-opt-active:hover {
3553
+ background: #e0e7ff;
3554
+ }
3555
+
3556
+ .ds-theme-dark .ds-dd-opt {
3557
+ color: #cbd5e1;
3558
+ }
3559
+
3560
+ .ds-theme-dark .ds-dd-opt:hover {
3561
+ background: #0f172a;
3562
+ }
3563
+
3564
+ .ds-theme-dark .ds-dd-opt-active {
3565
+ background: #1e293b;
3566
+ color: #818cf8;
3567
+ }
3568
+
3569
+ .ds-dd-opt-text {
3570
+ flex: 1;
3571
+ overflow: hidden;
3572
+ text-overflow: ellipsis;
3573
+ white-space: nowrap;
3574
+ }
3575
+
3576
+ .ds-dd-check {
3577
+ color: #4338ca;
3578
+ flex-shrink: 0;
3579
+ }
3580
+
3581
+ .ds-theme-dark .ds-dd-check {
3582
+ color: #818cf8;
3583
+ }
3584
+
3585
+ /* Empty / loading message */
3586
+ .ds-dd-msg {
3587
+ display: flex;
3588
+ align-items: center;
3589
+ justify-content: center;
3590
+ gap: 8px;
3591
+ padding: 14px 12px;
3592
+ font-size: 12px;
3593
+ color: #9ca3af;
3594
+ }
3595
+
3596
+ .ds-theme-dark .ds-dd-msg {
3597
+ color: #64748b;
3598
+ }
3599
+
3600
+ /* Spinner inside dropdown */
3601
+ .ds-dd-spinner {
3602
+ width: 14px;
3603
+ height: 14px;
3604
+ border: 2px solid #e5e7eb;
3605
+ border-top-color: #6366f1;
3606
+ border-radius: 50%;
3607
+ animation: ds-spin 0.8s linear infinite;
3608
+ flex-shrink: 0;
3609
+ }
3610
+
3611
+ /* ── v2: Inline filter (selection-filter placed in-grid) ───────────────────── */
3612
+
3613
+ .ds-inline-filter {
3614
+ width: 100%;
3615
+ height: 100%;
3616
+ display: flex;
3617
+ flex-direction: column;
3618
+ justify-content: center;
3619
+ gap: 4px;
3620
+ padding: 6px 10px;
3621
+ }
3622
+
3623
+ .ds-inline-filter-select,
3624
+ .ds-inline-filter-input {
3625
+ width: 100%;
3626
+ height: 34px;
3627
+ padding: 0 10px;
3628
+ border: 1px solid #d1d5db;
3629
+ border-radius: 6px;
3630
+ font-size: 13px;
3631
+ background: #ffffff;
3632
+ color: #111827;
3633
+ cursor: pointer;
3634
+ outline: none;
3635
+ transition: border-color 0.15s, box-shadow 0.15s;
3636
+ }
3637
+
3638
+ .ds-inline-filter-select:focus,
3639
+ .ds-inline-filter-input:focus {
3640
+ border-color: #6366f1;
3641
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
3642
+ }
3643
+
3644
+ .ds-theme-dark .ds-inline-filter-select,
3645
+ .ds-theme-dark .ds-inline-filter-input {
3646
+ background: #1e293b;
3647
+ border-color: #334155;
3648
+ color: #f1f5f9;
3649
+ }
3650
+
3651
+ /* ── Export toolbar ────────────────────────────────────────────────────────── */
3652
+ .ds-toolbar {
3653
+ display: flex;
3654
+ justify-content: flex-end;
3655
+ padding: 8px 16px 0;
3656
+ gap: 8px;
3657
+ }
3658
+
3659
+ .ds-toolbar-btn {
3660
+ height: 30px;
3661
+ padding: 0 12px;
3662
+ border: 1px solid #d1d5db;
3663
+ border-radius: 6px;
3664
+ font-size: 12px;
3665
+ font-weight: 500;
3666
+ background: #ffffff;
3667
+ color: #374151;
3668
+ cursor: pointer;
3669
+ display: flex;
3670
+ align-items: center;
3671
+ gap: 5px;
3672
+ transition: background 0.15s, border-color 0.15s;
3673
+ }
3674
+
3675
+ .ds-toolbar-btn:hover {
3676
+ background: #f9fafb;
3677
+ border-color: #9ca3af;
3678
+ }
3679
+
3680
+ .ds-theme-dark .ds-toolbar-btn {
3681
+ background: #1e293b;
3682
+ border-color: #334155;
3683
+ color: #e2e8f0;
3684
+ }
3685
+
3686
+ .ds-theme-dark .ds-toolbar-btn:hover {
3687
+ background: #0f172a;
3688
+ }
3689
+
3690
+ /* ─────────────────────────────────────────────────────────────────────────────
3691
+ v3 Badge-bar filter panel
3692
+ Layout: [⊻ Filters:] [Filter A] [Filter B] [+ Add Filter]
3693
+ Per-filter popovers open below the clicked badge.
3694
+ ───────────────────────────────────────────────────────────────────────────── */
3695
+
3696
+ /* Wrapper — relative so popovers can be absolutely positioned inside */
3697
+ .ds-fp-v3 {
3698
+ position: relative;
3699
+ border-bottom: 1px solid #e5e7eb;
3700
+ background: inherit;
3701
+ }
3702
+
3703
+ .ds-theme-dark .ds-fp-v3 {
3704
+ border-bottom-color: #1e293b;
3705
+ }
3706
+
3707
+ /* ── Horizontal bar ─────────────────────────────────────────────────────────── */
3708
+ .ds-fp-v3-bar {
3709
+ display: flex;
3710
+ align-items: center;
3711
+ flex-wrap: wrap;
3712
+ gap: 6px;
3713
+ padding: 8px 16px;
3714
+ }
3715
+
3716
+ /* "⊻ Filters:" label */
3717
+ .ds-fp-v3-label {
3718
+ display: inline-flex;
3719
+ align-items: center;
3720
+ gap: 5px;
3721
+ font-size: 12px;
3722
+ font-weight: 600;
3723
+ color: #6b7280;
3724
+ letter-spacing: 0.03em;
3725
+ white-space: nowrap;
3726
+ flex-shrink: 0;
3727
+ margin-right: 2px;
3728
+ }
3729
+
3730
+ .ds-fp-v3-label svg {
3731
+ color: #9ca3af;
3732
+ }
3733
+
3734
+ .ds-theme-dark .ds-fp-v3-label {
3735
+ color: #94a3b8;
3736
+ }
3737
+
3738
+ /* Container for all badge pills */
3739
+ .ds-fp-v3-badges {
3740
+ display: flex;
3741
+ flex-wrap: wrap;
3742
+ align-items: center;
3743
+ gap: 6px;
3744
+ }
3745
+
3746
+ /* ── Filter badge pill ──────────────────────────────────────────────────────── */
3747
+ .ds-fp-v3-badge {
3748
+ display: inline-flex;
3749
+ align-items: center;
3750
+ gap: 5px;
3751
+ padding: 4px 12px;
3752
+ border: 1.5px solid #d1d5db;
3753
+ border-radius: 20px;
3754
+ background: #ffffff;
3755
+ color: #374151;
3756
+ font-size: 12px;
3757
+ font-weight: 500;
3758
+ cursor: pointer;
3759
+ white-space: nowrap;
3760
+ transition: border-color 0.15s, background 0.15s, color 0.15s, box-shadow 0.15s;
3761
+ line-height: 1.4;
3762
+ }
3763
+
3764
+ .ds-fp-v3-badge svg {
3765
+ flex-shrink: 0;
3766
+ color: #9ca3af;
3767
+ transition: color 0.15s;
3768
+ }
3769
+
3770
+ .ds-fp-v3-badge:hover {
3771
+ border-color: #6366f1;
3772
+ background: #f5f3ff;
3773
+ color: #4338ca;
3774
+ }
3775
+
3776
+ .ds-fp-v3-badge:hover svg {
3777
+ color: #6366f1;
3778
+ }
3779
+
3780
+ .ds-theme-dark .ds-fp-v3-badge {
3781
+ background: #1e293b;
3782
+ border-color: #334155;
3783
+ color: #cbd5e1;
3784
+ }
3785
+
3786
+ .ds-theme-dark .ds-fp-v3-badge:hover {
3787
+ border-color: #6366f1;
3788
+ background: #1e1e3a;
3789
+ color: #a5b4fc;
3790
+ }
3791
+
3792
+ /* Active state — green filled pill */
3793
+ .ds-fp-v3-badge-active {
3794
+ border-color: #16a34a;
3795
+ background: #16a34a;
3796
+ color: #ffffff;
3797
+ }
3798
+
3799
+ .ds-fp-v3-badge-active svg {
3800
+ color: rgba(255, 255, 255, 0.8);
3801
+ }
3802
+
3803
+ .ds-fp-v3-badge-active:hover {
3804
+ border-color: #15803d;
3805
+ background: #15803d;
3806
+ color: #ffffff;
3807
+ }
3808
+
3809
+ .ds-fp-v3-badge-active:hover svg {
3810
+ color: rgba(255, 255, 255, 0.9);
3811
+ }
3812
+
3813
+ .ds-theme-dark .ds-fp-v3-badge-active {
3814
+ border-color: #16a34a;
3815
+ background: #16a34a;
3816
+ color: #ffffff;
3817
+ }
3818
+
3819
+ /* "+ Add Filter" ghost button */
3820
+ .ds-fp-v3-add {
3821
+ display: inline-flex;
3822
+ align-items: center;
3823
+ gap: 4px;
3824
+ padding: 4px 12px;
3825
+ border: 1.5px dashed #d1d5db;
3826
+ border-radius: 20px;
3827
+ background: transparent;
3828
+ color: #9ca3af;
3829
+ font-size: 12px;
3830
+ font-weight: 500;
3831
+ cursor: pointer;
3832
+ white-space: nowrap;
3833
+ transition: border-color 0.15s, color 0.15s;
3834
+ line-height: 1.4;
3835
+ }
3836
+
3837
+ .ds-fp-v3-add:hover {
3838
+ border-color: #6366f1;
3839
+ color: #6366f1;
3840
+ }
3841
+
3842
+ .ds-theme-dark .ds-fp-v3-add {
3843
+ border-color: #334155;
3844
+ color: #64748b;
3845
+ }
3846
+
3847
+ .ds-theme-dark .ds-fp-v3-add:hover {
3848
+ border-color: #6366f1;
3849
+ color: #818cf8;
3850
+ }
3851
+
3852
+ /* ── Popover ────────────────────────────────────────────────────────────────── */
3853
+ .ds-fp-v3-pop {
3854
+ position: absolute;
3855
+ z-index: 9999;
3856
+ width: 300px;
3857
+ background: #ffffff;
3858
+ border: 1px solid #e5e7eb;
3859
+ border-radius: 12px;
3860
+ box-shadow: 0 8px 28px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);
3861
+ overflow: hidden;
3862
+ }
3863
+
3864
+ .ds-theme-dark .ds-fp-v3-pop {
3865
+ background: #1e293b;
3866
+ border-color: #334155;
3867
+ box-shadow: 0 8px 28px rgba(0, 0, 0, 0.4);
3868
+ }
3869
+
3870
+ /* Popover header */
3871
+ .ds-fp-v3-pop-hd {
3872
+ display: flex;
3873
+ align-items: center;
3874
+ justify-content: space-between;
3875
+ padding: 10px 12px 10px 14px;
3876
+ border-bottom: 1px solid #f3f4f6;
3877
+ background: #fafafa;
3878
+ }
3879
+
3880
+ .ds-theme-dark .ds-fp-v3-pop-hd {
3881
+ background: #0f172a;
3882
+ border-bottom-color: #334155;
3883
+ }
3884
+
3885
+ .ds-fp-v3-pop-title {
3886
+ display: inline-flex;
3887
+ align-items: center;
3888
+ gap: 6px;
3889
+ font-size: 13px;
3890
+ font-weight: 600;
3891
+ color: #374151;
3892
+ }
3893
+
3894
+ .ds-fp-v3-pop-title svg {
3895
+ color: #6b7280;
3896
+ flex-shrink: 0;
3897
+ }
3898
+
3899
+ .ds-theme-dark .ds-fp-v3-pop-title {
3900
+ color: #e2e8f0;
3901
+ }
3902
+
3903
+ .ds-theme-dark .ds-fp-v3-pop-title svg {
3904
+ color: #94a3b8;
3905
+ }
3906
+
3907
+ /* Action icon buttons (trash + close) */
3908
+ .ds-fp-v3-pop-actions {
3909
+ display: flex;
3910
+ align-items: center;
3911
+ gap: 4px;
3912
+ }
3913
+
3914
+ .ds-fp-v3-pop-icon-btn {
3915
+ width: 26px;
3916
+ height: 26px;
3917
+ display: flex;
3918
+ align-items: center;
3919
+ justify-content: center;
3920
+ border: 1px solid #e5e7eb;
3921
+ border-radius: 6px;
3922
+ background: transparent;
3923
+ color: #6b7280;
3924
+ cursor: pointer;
3925
+ padding: 0;
3926
+ transition: border-color 0.15s, color 0.15s, background 0.15s;
3927
+ flex-shrink: 0;
3928
+ }
3929
+
3930
+ .ds-fp-v3-pop-icon-btn:hover {
3931
+ border-color: #6366f1;
3932
+ color: #6366f1;
3933
+ background: #eef2ff;
3934
+ }
3935
+
3936
+ .ds-theme-dark .ds-fp-v3-pop-icon-btn {
3937
+ border-color: #334155;
3938
+ color: #94a3b8;
3939
+ }
3940
+
3941
+ .ds-theme-dark .ds-fp-v3-pop-icon-btn:hover {
3942
+ border-color: #6366f1;
3943
+ color: #818cf8;
3944
+ background: #1e1e3a;
3945
+ }
3946
+
3947
+ /* Popover body */
3948
+ .ds-fp-v3-pop-body {
3949
+ display: flex;
3950
+ flex-direction: column;
3951
+ gap: 10px;
3952
+ padding: 12px 14px;
3953
+ }
3954
+
3955
+ /* Field row (label + control) */
3956
+ .ds-fp-v3-field {
3957
+ display: flex;
3958
+ flex-direction: column;
3959
+ gap: 5px;
3960
+ }
3961
+
3962
+ .ds-fp-v3-field-lbl {
3963
+ font-size: 10px;
3964
+ font-weight: 700;
3965
+ text-transform: uppercase;
3966
+ letter-spacing: 0.06em;
3967
+ color: #9ca3af;
3968
+ }
3969
+
3970
+ .ds-theme-dark .ds-fp-v3-field-lbl {
3971
+ color: #64748b;
3972
+ }
3973
+
3974
+ /* Condition select */
3975
+ .ds-fp-v3-condition {
3976
+ width: 100%;
3977
+ height: 32px;
3978
+ padding: 0 8px;
3979
+ border: 1px solid #d1d5db;
3980
+ border-radius: 7px;
3981
+ font-size: 12px;
3982
+ background: #ffffff;
3983
+ color: #374151;
3984
+ cursor: pointer;
3985
+ outline: none;
3986
+ transition: border-color 0.15s;
3987
+ }
3988
+
3989
+ .ds-fp-v3-condition:focus {
3990
+ border-color: #6366f1;
3991
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15);
3992
+ }
3993
+
3994
+ .ds-theme-dark .ds-fp-v3-condition {
3995
+ background: #0f172a;
3996
+ border-color: #334155;
3997
+ color: #e2e8f0;
3998
+ }
3999
+
4000
+ /* ── Multi-select widget ────────────────────────────────────────────────────── */
4001
+ .ds-fp-v3-ms-wrap {
4002
+ display: flex;
4003
+ flex-direction: column;
4004
+ gap: 6px;
4005
+ }
4006
+
4007
+ /* Trigger row */
4008
+ .ds-fp-v3-ms-trigger {
4009
+ display: flex;
4010
+ align-items: center;
4011
+ justify-content: space-between;
4012
+ gap: 6px;
4013
+ height: 32px;
4014
+ padding: 0 10px;
4015
+ border: 1px solid #d1d5db;
4016
+ border-radius: 7px;
4017
+ background: #ffffff;
4018
+ cursor: pointer;
4019
+ font-size: 12px;
4020
+ color: #374151;
4021
+ transition: border-color 0.15s;
4022
+ user-select: none;
4023
+ }
4024
+
4025
+ .ds-fp-v3-ms-trigger:hover {
4026
+ border-color: #6366f1;
4027
+ }
4028
+
4029
+ .ds-theme-dark .ds-fp-v3-ms-trigger {
4030
+ background: #0f172a;
4031
+ border-color: #334155;
4032
+ color: #e2e8f0;
4033
+ }
4034
+
4035
+ .ds-fp-v3-ms-display {
4036
+ flex: 1;
4037
+ overflow: hidden;
4038
+ text-overflow: ellipsis;
4039
+ white-space: nowrap;
4040
+ color: inherit;
4041
+ }
4042
+
4043
+ /* When no value selected, show placeholder colour */
4044
+ .ds-fp-v3-ms-trigger:not(.ds-fp-v3-ms-has-value) .ds-fp-v3-ms-display {
4045
+ color: #9ca3af;
4046
+ }
4047
+
4048
+ /* Dropdown panel */
4049
+ .ds-fp-v3-ms-drop {
4050
+ border: 1px solid #e5e7eb;
4051
+ border-radius: 8px;
4052
+ overflow: hidden;
4053
+ background: #ffffff;
4054
+ }
4055
+
4056
+ .ds-theme-dark .ds-fp-v3-ms-drop {
4057
+ background: #0f172a;
4058
+ border-color: #334155;
4059
+ }
4060
+
4061
+ /* Search input inside dropdown */
4062
+ .ds-fp-v3-ms-search {
4063
+ width: 100%;
4064
+ height: 32px;
4065
+ padding: 0 10px;
4066
+ border: none;
4067
+ border-bottom: 1px solid #f3f4f6;
4068
+ outline: none;
4069
+ font-size: 12px;
4070
+ background: #f9fafb;
4071
+ color: #374151;
4072
+ transition: background 0.15s;
4073
+ }
4074
+
4075
+ .ds-fp-v3-ms-search:focus {
4076
+ background: #ffffff;
4077
+ border-bottom-color: #6366f1;
4078
+ }
4079
+
4080
+ .ds-theme-dark .ds-fp-v3-ms-search {
4081
+ background: #0f172a;
4082
+ border-bottom-color: #1e293b;
4083
+ color: #e2e8f0;
4084
+ }
4085
+
4086
+ .ds-fp-v3-ms-search::placeholder {
4087
+ color: #9ca3af;
4088
+ }
4089
+
4090
+ /* Options list */
4091
+ .ds-fp-v3-ms-opts {
4092
+ max-height: 160px;
4093
+ overflow-y: auto;
4094
+ }
4095
+
4096
+ /* Individual option row */
4097
+ .ds-fp-v3-opt {
4098
+ display: flex;
4099
+ align-items: center;
4100
+ gap: 8px;
4101
+ padding: 6px 10px;
4102
+ cursor: pointer;
4103
+ font-size: 12px;
4104
+ color: #374151;
4105
+ transition: background 0.1s;
4106
+ user-select: none;
4107
+ }
4108
+
4109
+ .ds-fp-v3-opt:hover {
4110
+ background: #f0f0ff;
4111
+ }
4112
+
4113
+ .ds-fp-v3-opt-on {
4114
+ background: #eef2ff;
4115
+ }
4116
+
4117
+ .ds-theme-dark .ds-fp-v3-opt {
4118
+ color: #cbd5e1;
4119
+ }
4120
+
4121
+ .ds-theme-dark .ds-fp-v3-opt:hover {
4122
+ background: #1e293b;
4123
+ }
4124
+
4125
+ .ds-theme-dark .ds-fp-v3-opt-on {
4126
+ background: #1e293b;
4127
+ }
4128
+
4129
+ /* Custom checkbox */
4130
+ .ds-fp-v3-cb {
4131
+ width: 15px;
4132
+ height: 15px;
4133
+ border: 2px solid #d1d5db;
4134
+ border-radius: 3px;
4135
+ flex-shrink: 0;
4136
+ display: flex;
4137
+ align-items: center;
4138
+ justify-content: center;
4139
+ transition: border-color 0.1s, background 0.1s;
4140
+ }
4141
+
4142
+ .ds-fp-v3-cb-on {
4143
+ border-color: #6366f1;
4144
+ background: #6366f1;
4145
+ }
4146
+
4147
+ .ds-fp-v3-cb-on svg {
4148
+ stroke: #fff;
4149
+ }
4150
+
4151
+ /* Options empty/loading message */
4152
+ .ds-fp-v3-opts-msg {
4153
+ display: flex;
4154
+ align-items: center;
4155
+ justify-content: center;
4156
+ gap: 8px;
4157
+ padding: 12px;
4158
+ font-size: 12px;
4159
+ color: #9ca3af;
4160
+ }
4161
+
4162
+ /* ── Chips row (selected values) ────────────────────────────────────────────── */
4163
+ .ds-fp-v3-chips {
4164
+ display: flex;
4165
+ flex-wrap: wrap;
4166
+ gap: 4px;
4167
+ }
4168
+
4169
+ .ds-fp-v3-chip {
4170
+ display: inline-flex;
4171
+ align-items: center;
4172
+ gap: 4px;
4173
+ padding: 2px 8px;
4174
+ border-radius: 10px;
4175
+ background: #e0e7ff;
4176
+ color: #4338ca;
4177
+ font-size: 11px;
4178
+ font-weight: 600;
4179
+ white-space: nowrap;
4180
+ }
4181
+
4182
+ .ds-theme-dark .ds-fp-v3-chip {
4183
+ background: #312e81;
4184
+ color: #a5b4fc;
4185
+ }
4186
+
4187
+ .ds-fp-v3-chip-x {
4188
+ border: none;
4189
+ background: transparent;
4190
+ color: inherit;
4191
+ font-size: 10px;
4192
+ cursor: pointer;
4193
+ padding: 0;
4194
+ line-height: 1;
4195
+ opacity: 0.7;
4196
+ transition: opacity 0.1s;
4197
+ }
4198
+
4199
+ .ds-fp-v3-chip-x:hover {
4200
+ opacity: 1;
4201
+ }
4202
+
4203
+ /* ── Popover footer ─────────────────────────────────────────────────────────── */
4204
+ .ds-fp-v3-pop-footer {
4205
+ display: flex;
4206
+ align-items: center;
4207
+ justify-content: flex-end;
4208
+ gap: 8px;
4209
+ padding: 10px 14px;
4210
+ border-top: 1px solid #f3f4f6;
4211
+ background: #fafafa;
4212
+ }
4213
+
4214
+ .ds-theme-dark .ds-fp-v3-pop-footer {
4215
+ background: #0f172a;
4216
+ border-top-color: #334155;
4217
+ }
4218
+
4219
+ .ds-fp-v3-clear-btn {
4220
+ height: 30px;
4221
+ padding: 0 14px;
4222
+ border: 1px solid #d1d5db;
4223
+ border-radius: 7px;
4224
+ background: #ffffff;
4225
+ color: #374151;
4226
+ font-size: 12px;
4227
+ font-weight: 500;
4228
+ cursor: pointer;
4229
+ transition: border-color 0.15s, background 0.15s;
4230
+ }
4231
+
4232
+ .ds-fp-v3-clear-btn:hover {
4233
+ border-color: #9ca3af;
4234
+ background: #f3f4f6;
4235
+ }
4236
+
4237
+ .ds-theme-dark .ds-fp-v3-clear-btn {
4238
+ background: #1e293b;
4239
+ border-color: #334155;
4240
+ color: #e2e8f0;
4241
+ }
4242
+
4243
+ .ds-theme-dark .ds-fp-v3-clear-btn:hover {
4244
+ background: #0f172a;
4245
+ }
4246
+
4247
+ .ds-fp-v3-apply-btn {
4248
+ height: 30px;
4249
+ padding: 0 16px;
4250
+ border: none;
4251
+ border-radius: 7px;
4252
+ background: linear-gradient(135deg, #16a34a, #15803d);
4253
+ color: #ffffff;
4254
+ font-size: 12px;
4255
+ font-weight: 600;
4256
+ cursor: pointer;
4257
+ transition: opacity 0.15s, transform 0.1s;
4258
+ white-space: nowrap;
4259
+ }
4260
+
4261
+ .ds-fp-v3-apply-btn:hover {
4262
+ opacity: 0.9;
4263
+ }
4264
+
4265
+ .ds-fp-v3-apply-btn:active {
4266
+ transform: scale(0.97);
4267
+ }
4268
+
4269
+ /* ── Date range inputs ──────────────────────────────────────────────────────── */
4270
+ .ds-fp-v3-date-range {
4271
+ display: flex;
4272
+ align-items: center;
4273
+ gap: 6px;
4274
+ }
4275
+
4276
+ .ds-fp-v3-date {
4277
+ flex: 1;
4278
+ height: 32px;
4279
+ padding: 0 8px;
4280
+ border: 1px solid #d1d5db;
4281
+ border-radius: 7px;
4282
+ font-size: 12px;
4283
+ background: #ffffff;
4284
+ color: #374151;
4285
+ outline: none;
4286
+ transition: border-color 0.15s;
4287
+ }
4288
+
4289
+ .ds-fp-v3-date:focus {
4290
+ border-color: #6366f1;
4291
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15);
4292
+ }
4293
+
4294
+ .ds-theme-dark .ds-fp-v3-date {
4295
+ background: #0f172a;
4296
+ border-color: #334155;
4297
+ color: #e2e8f0;
4298
+ }
4299
+
4300
+ .ds-fp-v3-date-sep {
4301
+ color: #9ca3af;
4302
+ font-size: 12px;
4303
+ flex-shrink: 0;
4304
+ }
4305
+
4306
+ /* ── Text / number input ────────────────────────────────────────────────────── */
4307
+ .ds-fp-v3-text-input {
4308
+ width: 100%;
4309
+ height: 32px;
4310
+ padding: 0 10px;
4311
+ border: 1px solid #d1d5db;
4312
+ border-radius: 7px;
4313
+ font-size: 12px;
4314
+ background: #ffffff;
4315
+ color: #374151;
4316
+ outline: none;
4317
+ transition: border-color 0.15s;
4318
+ }
4319
+
4320
+ .ds-fp-v3-text-input:focus {
4321
+ border-color: #6366f1;
4322
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15);
4323
+ }
4324
+
4325
+ .ds-theme-dark .ds-fp-v3-text-input {
4326
+ background: #0f172a;
4327
+ border-color: #334155;
4328
+ color: #e2e8f0;
4329
+ }
4330
+ `, e = document.createElement("style");
4331
+ e.id = "deepspot-sdk-styles", e.textContent = A, document.head.appendChild(e);
4332
+ } catch (A) {
4333
+ }
4334
+ }
4335
+ }
4336
+ function de(A) {
4337
+ if (typeof A == "string") {
4338
+ const e = document.querySelector(A);
4339
+ if (!e) throw new Error(`Deepspot SDK: container "${A}" not found in DOM`);
4340
+ return e;
4341
+ }
4342
+ return A;
4343
+ }
4344
+ class ue {
2060
4345
  constructor(e) {
2061
4346
  if (!e.apiKey) throw new Error("Deepspot SDK: apiKey is required");
2062
4347
  if (!e.baseUrl) throw new Error("Deepspot SDK: baseUrl is required");
2063
- this.apiClient = new K(e.baseUrl, e.apiKey), q();
4348
+ this.apiClient = new B(e.baseUrl, e.apiKey), K();
2064
4349
  }
2065
4350
  // ── Embed full dashboard ────────────────────────────────────────────────────
2066
4351
  async embedDashboard(e) {
2067
- var a, o, l, d, h, b, n, p, f, m;
2068
- const t = de(e.container), s = (a = e.embedLevel) != null ? a : "dashboard", i = (o = e.theme) != null ? o : "light";
4352
+ var i, a, f, h, v, m, p, n, g, u;
4353
+ const t = de(e.container), s = (i = e.embedLevel) != null ? i : "dashboard", r = (a = e.theme) != null ? a : "light";
2069
4354
  t.style.height = e.height || "600px", t.style.display = "block";
2070
- const r = this.createRoot(t, i);
2071
- this.showLoading(r);
4355
+ const o = this.createRoot(t, r);
4356
+ this.showLoading(o);
2072
4357
  try {
2073
- const c = await this.apiClient.getEmbedToken({
4358
+ const b = await this.apiClient.getEmbedToken({
2074
4359
  dashboardId: e.dashboardId,
2075
4360
  embedType: "dashboard",
2076
4361
  embedLevel: s,
2077
4362
  userId: e.userId,
2078
4363
  tenantId: e.tenantId
2079
- }), u = await this.apiClient.getDashboardRender(
4364
+ }), c = await this.apiClient.getDashboardRender(
2080
4365
  e.dashboardId,
2081
- c,
4366
+ b,
2082
4367
  {
2083
4368
  embedLevel: s,
2084
4369
  pageId: e.pageId,
@@ -2086,91 +4371,94 @@ class ge {
2086
4371
  filters: e.filters || {}
2087
4372
  }
2088
4373
  );
2089
- let g;
2090
- const k = new D(r, {
4374
+ let d;
4375
+ const l = new j(o, {
2091
4376
  embedLevel: s,
2092
- theme: i,
4377
+ theme: r,
2093
4378
  // v1 backward compat (hideFilters → hideGlobalFilters)
2094
- hideFilters: (l = e.hideGlobalFilters) != null ? l : !1,
2095
- hideExport: (d = e.hideExport) != null ? d : !1,
4379
+ hideFilters: (f = e.hideGlobalFilters) != null ? f : !1,
4380
+ hideExport: (h = e.hideExport) != null ? h : !1,
2096
4381
  // v2 visibility
2097
- hideGlobalFilters: (h = e.hideGlobalFilters) != null ? h : !1,
2098
- hideInlineFilters: (b = e.hideInlineFilters) != null ? b : !1,
2099
- hideText: (n = e.hideText) != null ? n : !1,
2100
- hideHeader: (p = e.hideHeader) != null ? p : !1,
2101
- hideBackground: (f = e.hideBackground) != null ? f : !1,
4382
+ hideGlobalFilters: (v = e.hideGlobalFilters) != null ? v : !1,
4383
+ hideInlineFilters: (m = e.hideInlineFilters) != null ? m : !1,
4384
+ hideText: (p = e.hideText) != null ? p : !1,
4385
+ hideHeader: (n = e.hideHeader) != null ? n : !1,
4386
+ hideBackground: (g = e.hideBackground) != null ? g : !1,
2102
4387
  initialFilters: e.filters || {},
2103
4388
  onFilterChange: e.onFilterChange,
2104
4389
  onReady: e.onReady,
2105
- onFetchFilterOptions: (T) => this.apiClient.getFilterOptions(e.dashboardId, T, c),
2106
- onFetchTablePage: (T, C, $) => this.apiClient.getTablePage(e.dashboardId, T, C, $, c),
2107
- onTabSwitch: async (T, C) => {
2108
- var $, R, I;
2109
- ($ = e.onTabSwitch) == null || $.call(e, T, C);
4390
+ onFetchFilterOptions: (T) => this.apiClient.getFilterOptions(e.dashboardId, T, b),
4391
+ onFetchTablePage: (T, w, k) => this.apiClient.getTablePage(e.dashboardId, T, w, k, b),
4392
+ onTabSwitch: async (T, w) => {
4393
+ var k;
4394
+ (k = e.onTabSwitch) == null || k.call(e, T, w);
2110
4395
  try {
2111
- const P = await this.apiClient.getDashboardRender(
4396
+ const x = await this.apiClient.getDashboardRender(
2112
4397
  e.dashboardId,
2113
- c,
4398
+ b,
2114
4399
  {
2115
4400
  embedLevel: s,
2116
4401
  pageId: T,
2117
- tabId: C,
2118
- filters: (I = (R = g == null ? void 0 : g.getActiveFilters()) != null ? R : e.filters) != null ? I : {}
4402
+ tabId: w,
4403
+ // Use renderer's live filter state instance.filters is a stale copy
4404
+ filters: l.getActiveFilters(),
4405
+ filterOperators: l.getActiveFilterOperators(),
4406
+ localFilters: l.getActiveLocalFilters()
2119
4407
  }
2120
4408
  );
2121
- k.update(P);
2122
- } catch (P) {
2123
- console.error("Deepspot SDK: tab fetch failed", P);
4409
+ l.update(x);
4410
+ } catch (x) {
4411
+ console.error("Deepspot SDK: tab fetch failed", x);
2124
4412
  }
2125
4413
  }
2126
4414
  });
2127
- return k.render(u), g = new E({
4415
+ return l.render(c), d = new z({
2128
4416
  dashboardId: e.dashboardId,
2129
- token: c,
4417
+ token: b,
2130
4418
  embedType: "dashboard",
2131
4419
  embedLevel: s,
2132
- activePageId: u.activePage,
2133
- activeTabId: u.activeTab,
4420
+ activePageId: c.activePage,
4421
+ activeTabId: c.activeTab,
2134
4422
  activeFilters: e.filters || {},
2135
4423
  apiClient: this.apiClient,
2136
- renderer: k,
4424
+ renderer: l,
2137
4425
  onFilterChange: e.onFilterChange
2138
- }), g;
2139
- } catch (c) {
2140
- throw this.showError(r, (c == null ? void 0 : c.message) || "Failed to load dashboard"), (m = e.onError) == null || m.call(e, (c == null ? void 0 : c.message) || "Failed to load dashboard"), c;
4426
+ }), d;
4427
+ } catch (b) {
4428
+ throw this.showError(o, (b == null ? void 0 : b.message) || "Failed to load dashboard"), (u = e.onError) == null || u.call(e, (b == null ? void 0 : b.message) || "Failed to load dashboard"), b;
2141
4429
  }
2142
4430
  }
2143
4431
  // ── Embed single report (component) ────────────────────────────────────────
2144
4432
  async embedReport(e) {
2145
- var i;
4433
+ var r;
2146
4434
  const t = de(e.container);
2147
4435
  t.style.height = e.height || "400px", t.style.display = "block";
2148
4436
  const s = this.createRoot(t, e.theme || "light");
2149
4437
  this.showLoading(s);
2150
4438
  try {
2151
- const r = await this.apiClient.getEmbedToken({
4439
+ const o = await this.apiClient.getEmbedToken({
2152
4440
  dashboardId: e.dashboardId,
2153
4441
  embedType: "report",
2154
4442
  componentId: e.componentId,
2155
4443
  userId: e.userId,
2156
4444
  tenantId: e.tenantId
2157
- }), a = await this.apiClient.getReportRender(
4445
+ }), i = await this.apiClient.getReportRender(
2158
4446
  e.dashboardId,
2159
4447
  e.componentId,
2160
- r,
4448
+ o,
2161
4449
  { filters: e.filters || {} }
2162
- ), o = new he(s, e.theme || "light");
2163
- return o.render(a, e.onReady), new E({
4450
+ ), a = new pe(s, e.theme || "light");
4451
+ return a.render(i, e.onReady), new z({
2164
4452
  dashboardId: e.dashboardId,
2165
4453
  componentId: e.componentId,
2166
- token: r,
4454
+ token: o,
2167
4455
  embedType: "report",
2168
4456
  activeFilters: e.filters || {},
2169
4457
  apiClient: this.apiClient,
2170
- renderer: o
4458
+ renderer: a
2171
4459
  });
2172
- } catch (r) {
2173
- throw this.showError(s, (r == null ? void 0 : r.message) || "Failed to load report"), (i = e.onError) == null || i.call(e, (r == null ? void 0 : r.message) || "Failed to load report"), r;
4460
+ } catch (o) {
4461
+ throw this.showError(s, (o == null ? void 0 : o.message) || "Failed to load report"), (r = e.onError) == null || r.call(e, (o == null ? void 0 : o.message) || "Failed to load report"), o;
2174
4462
  }
2175
4463
  }
2176
4464
  // ── Private helpers ─────────────────────────────────────────────────────────
@@ -2197,83 +4485,83 @@ class ge {
2197
4485
  `;
2198
4486
  }
2199
4487
  }
2200
- const B = class B extends HTMLElement {
4488
+ const _ = class _ extends HTMLElement {
2201
4489
  constructor() {
2202
4490
  super(...arguments), this.instance = null, this.connected = !1;
2203
4491
  }
2204
4492
  connectedCallback() {
2205
- this.connected = !0, q(), this.style.display = "block", this.renderRoot = document.createElement("div"), this.renderRoot.className = "ds-embed-root", this.appendChild(this.renderRoot), this.mount();
4493
+ this.connected = !0, K(), this.style.display = "block", this.renderRoot = document.createElement("div"), this.renderRoot.className = "ds-embed-root", this.appendChild(this.renderRoot), this.mount();
2206
4494
  }
2207
4495
  disconnectedCallback() {
2208
4496
  var e;
2209
4497
  this.connected = !1, (e = this.instance) == null || e.destroy(), this.instance = null;
2210
4498
  }
2211
4499
  attributeChangedCallback(e, t, s) {
2212
- var i;
2213
- t === s || !this.connected || ((i = this.instance) == null || i.destroy(), this.instance = null, this.mount());
4500
+ var r;
4501
+ t === s || !this.connected || ((r = this.instance) == null || r.destroy(), this.instance = null, this.mount());
2214
4502
  }
2215
4503
  async mount() {
2216
- const e = this.getAttribute("dashboard-id"), t = this.getAttribute("api-key"), s = this.getAttribute("base-url") || "", i = this.getAttribute("user-id") || void 0, r = this.getAttribute("tenant-id") || void 0, a = this.getAttribute("embed-level") || "dashboard", o = this.getAttribute("theme") || "light", l = this.getAttribute("height") || "600px", d = this.getAttribute("page-id") || void 0, h = this.getAttribute("tab-id") || void 0, b = this.getAttribute("hide-filters") === "true", n = this.getAttribute("hide-export") === "true", p = this.getAttribute("hide-global-filters") === "true" || b, f = this.getAttribute("hide-inline-filters") === "true", m = this.getAttribute("hide-text") === "true", c = this.getAttribute("hide-header") === "true", u = this.getAttribute("hide-background") === "true";
4504
+ const e = this.getAttribute("dashboard-id"), t = this.getAttribute("api-key"), s = this.getAttribute("base-url") || "", r = this.getAttribute("user-id") || void 0, o = this.getAttribute("tenant-id") || void 0, i = this.getAttribute("embed-level") || "dashboard", a = this.getAttribute("theme") || "light", f = this.getAttribute("height") || "600px", h = this.getAttribute("page-id") || void 0, v = this.getAttribute("tab-id") || void 0, m = this.getAttribute("hide-filters") === "true", p = this.getAttribute("hide-export") === "true", n = this.getAttribute("hide-global-filters") === "true" || m, g = this.getAttribute("hide-inline-filters") === "true", u = this.getAttribute("hide-text") === "true", b = this.getAttribute("hide-header") === "true", c = this.getAttribute("hide-background") === "true";
2217
4505
  if (!e || !t || !s) {
2218
4506
  this.showError("Missing required attributes: dashboard-id, api-key, base-url");
2219
4507
  return;
2220
4508
  }
2221
- this.renderRoot.style.height = l, this.renderRoot.className = `ds-embed-root ds-theme-${o}`, this.showLoading();
4509
+ this.renderRoot.style.height = f, this.renderRoot.className = `ds-embed-root ds-theme-${a}`, this.showLoading();
2222
4510
  try {
2223
- const g = new K(s, t), k = await g.getEmbedToken({
4511
+ const d = new B(s, t), l = await d.getEmbedToken({
2224
4512
  dashboardId: e,
2225
4513
  embedType: "dashboard",
2226
- embedLevel: a,
2227
- userId: i,
2228
- tenantId: r
2229
- }), T = await g.getDashboardRender(e, k, {
2230
- embedLevel: a,
2231
- pageId: d,
2232
- tabId: h,
4514
+ embedLevel: i,
4515
+ userId: r,
4516
+ tenantId: o
4517
+ }), T = await d.getDashboardRender(e, l, {
4518
+ embedLevel: i,
4519
+ pageId: h,
4520
+ tabId: v,
2233
4521
  filters: {}
2234
- }), C = new D(this.renderRoot, {
2235
- embedLevel: a,
2236
- theme: o,
4522
+ }), w = new j(this.renderRoot, {
4523
+ embedLevel: i,
4524
+ theme: a,
2237
4525
  // v1 backward compat
2238
- hideFilters: p,
2239
- hideExport: n,
4526
+ hideFilters: n,
4527
+ hideExport: p,
2240
4528
  // v2 visibility
2241
- hideGlobalFilters: p,
2242
- hideInlineFilters: f,
2243
- hideText: m,
2244
- hideHeader: c,
2245
- hideBackground: u,
4529
+ hideGlobalFilters: n,
4530
+ hideInlineFilters: g,
4531
+ hideText: u,
4532
+ hideHeader: b,
4533
+ hideBackground: c,
2246
4534
  initialFilters: {},
2247
- onFetchFilterOptions: ($) => g.getFilterOptions(e, $, k),
2248
- onFetchTablePage: ($, R, I) => g.getTablePage(e, $, R, I, k),
2249
- onTabSwitch: async ($, R) => {
2250
- var I, P;
4535
+ onFetchFilterOptions: (k) => d.getFilterOptions(e, k, l),
4536
+ onFetchTablePage: (k, x, $) => d.getTablePage(e, k, x, $, l),
4537
+ onTabSwitch: async (k, x) => {
4538
+ var $, y;
2251
4539
  try {
2252
- const L = await g.getDashboardRender(e, k, {
2253
- embedLevel: a,
2254
- pageId: $,
2255
- tabId: R,
2256
- filters: (P = (I = this.instance) == null ? void 0 : I.getActiveFilters()) != null ? P : {}
4540
+ const S = await d.getDashboardRender(e, l, {
4541
+ embedLevel: i,
4542
+ pageId: k,
4543
+ tabId: x,
4544
+ filters: (y = ($ = this.instance) == null ? void 0 : $.getActiveFilters()) != null ? y : {}
2257
4545
  });
2258
- C.update(L);
2259
- } catch (L) {
2260
- console.error("Deepspot SDK: tab fetch failed", L);
4546
+ w.update(S);
4547
+ } catch (S) {
4548
+ console.error("Deepspot SDK: tab fetch failed", S);
2261
4549
  }
2262
4550
  }
2263
4551
  });
2264
- C.render(T), this.instance = new E({
4552
+ w.render(T), this.instance = new z({
2265
4553
  dashboardId: e,
2266
- token: k,
2267
- embedLevel: a,
4554
+ token: l,
4555
+ embedLevel: i,
2268
4556
  embedType: "dashboard",
2269
4557
  activePageId: T.activePage,
2270
4558
  activeTabId: T.activeTab,
2271
4559
  activeFilters: {},
2272
- apiClient: g,
2273
- renderer: C
4560
+ apiClient: d,
4561
+ renderer: w
2274
4562
  });
2275
- } catch (g) {
2276
- this.showError((g == null ? void 0 : g.message) || "Failed to load dashboard");
4563
+ } catch (d) {
4564
+ this.showError((d == null ? void 0 : d.message) || "Failed to load dashboard");
2277
4565
  }
2278
4566
  }
2279
4567
  showLoading() {
@@ -2317,7 +4605,7 @@ const B = class B extends HTMLElement {
2317
4605
  (e = this.instance) == null || e.exportPDF();
2318
4606
  }
2319
4607
  };
2320
- B.observedAttributes = [
4608
+ _.observedAttributes = [
2321
4609
  "dashboard-id",
2322
4610
  "api-key",
2323
4611
  "user-id",
@@ -2338,52 +4626,52 @@ B.observedAttributes = [
2338
4626
  "hide-header",
2339
4627
  "hide-background"
2340
4628
  ];
2341
- let j = B;
2342
- customElements.get("deepspot-dashboard") || customElements.define("deepspot-dashboard", j);
4629
+ let V = _;
4630
+ customElements.get("deepspot-dashboard") || customElements.define("deepspot-dashboard", V);
2343
4631
  const G = class G extends HTMLElement {
2344
4632
  constructor() {
2345
4633
  super(...arguments), this.instance = null, this.isConnected_ = !1;
2346
4634
  }
2347
4635
  connectedCallback() {
2348
- this.isConnected_ = !0, q(), this.style.display = "block", this.renderRoot = document.createElement("div"), this.renderRoot.className = "ds-embed-root", this.appendChild(this.renderRoot), this.mount();
4636
+ this.isConnected_ = !0, K(), this.style.display = "block", this.renderRoot = document.createElement("div"), this.renderRoot.className = "ds-embed-root", this.appendChild(this.renderRoot), this.mount();
2349
4637
  }
2350
4638
  disconnectedCallback() {
2351
4639
  var e;
2352
4640
  this.isConnected_ = !1, (e = this.instance) == null || e.destroy(), this.instance = null;
2353
4641
  }
2354
4642
  attributeChangedCallback(e, t, s) {
2355
- var i;
2356
- t !== s && this.isConnected_ && ((i = this.instance) == null || i.destroy(), this.instance = null, this.mount());
4643
+ var r;
4644
+ t !== s && this.isConnected_ && ((r = this.instance) == null || r.destroy(), this.instance = null, this.mount());
2357
4645
  }
2358
4646
  async mount() {
2359
- const e = this.getAttribute("dashboard-id"), t = this.getAttribute("component-id"), s = this.getAttribute("api-key"), i = this.getAttribute("base-url") || "", r = this.getAttribute("user-id") || void 0, a = this.getAttribute("tenant-id") || void 0, o = this.getAttribute("theme") || "light", l = this.getAttribute("height") || "400px";
2360
- if (!e || !t || !s || !i) {
4647
+ const e = this.getAttribute("dashboard-id"), t = this.getAttribute("component-id"), s = this.getAttribute("api-key"), r = this.getAttribute("base-url") || "", o = this.getAttribute("user-id") || void 0, i = this.getAttribute("tenant-id") || void 0, a = this.getAttribute("theme") || "light", f = this.getAttribute("height") || "400px";
4648
+ if (!e || !t || !s || !r) {
2361
4649
  this.showError("Missing required attributes: dashboard-id, component-id, api-key, base-url");
2362
4650
  return;
2363
4651
  }
2364
- this.renderRoot.style.height = l, this.renderRoot.className = `ds-embed-root ds-theme-${o}`, this.showLoading();
4652
+ this.renderRoot.style.height = f, this.renderRoot.className = `ds-embed-root ds-theme-${a}`, this.showLoading();
2365
4653
  try {
2366
- const d = new K(i, s), h = await d.getEmbedToken({
4654
+ const h = new B(r, s), v = await h.getEmbedToken({
2367
4655
  dashboardId: e,
2368
4656
  embedType: "report",
2369
4657
  componentId: t,
2370
- userId: r,
2371
- tenantId: a
2372
- }), b = await d.getReportRender(e, t, h, {
4658
+ userId: o,
4659
+ tenantId: i
4660
+ }), m = await h.getReportRender(e, t, v, {
2373
4661
  filters: {}
2374
- }), n = new he(this.renderRoot, o);
2375
- n.render(b, () => {
2376
- }), this.instance = new E({
4662
+ }), p = new pe(this.renderRoot, a);
4663
+ p.render(m, () => {
4664
+ }), this.instance = new z({
2377
4665
  dashboardId: e,
2378
4666
  componentId: t,
2379
- token: h,
4667
+ token: v,
2380
4668
  embedType: "report",
2381
4669
  activeFilters: {},
2382
- apiClient: d,
2383
- renderer: n
4670
+ apiClient: h,
4671
+ renderer: p
2384
4672
  });
2385
- } catch (d) {
2386
- this.showError((d == null ? void 0 : d.message) || "Failed to load report");
4673
+ } catch (h) {
4674
+ this.showError((h == null ? void 0 : h.message) || "Failed to load report");
2387
4675
  }
2388
4676
  }
2389
4677
  showLoading() {
@@ -2426,10 +4714,10 @@ G.observedAttributes = [
2426
4714
  "theme",
2427
4715
  "height"
2428
4716
  ];
2429
- let z = G;
2430
- customElements.get("deepspot-report") || customElements.define("deepspot-report", z);
2431
- customElements.get("deepspot-dashboard") || customElements.define("deepspot-dashboard", j);
2432
- customElements.get("deepspot-report") || customElements.define("deepspot-report", z);
4717
+ let q = G;
4718
+ customElements.get("deepspot-report") || customElements.define("deepspot-report", q);
4719
+ customElements.get("deepspot-dashboard") || customElements.define("deepspot-dashboard", V);
4720
+ customElements.get("deepspot-report") || customElements.define("deepspot-report", q);
2433
4721
  export {
2434
- ge as DeepspotSDK
4722
+ ue as DeepspotSDK
2435
4723
  };