myio-js-library 0.1.516 → 0.1.518

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.
package/dist/index.cjs CHANGED
@@ -1208,7 +1208,7 @@ module.exports = __toCommonJS(index_exports);
1208
1208
  // package.json
1209
1209
  var package_default = {
1210
1210
  name: "myio-js-library",
1211
- version: "0.1.516",
1211
+ version: "0.1.518",
1212
1212
  description: "A clean, standalone JS SDK for MYIO projects",
1213
1213
  license: "MIT",
1214
1214
  repository: "github:gh-myio/myio-js-library",
@@ -20627,6 +20627,35 @@ var PANEL_CSS2 = `
20627
20627
  background: #ffffff;
20628
20628
  color: #2F5848;
20629
20629
  }
20630
+
20631
+ /* \u2500\u2500 Accordion (opt-in via options.collapsible) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
20632
+ The header (.myio-hp, from HeaderPanelComponent) becomes clickable and an
20633
+ arrow is shown before its title. Collapsed hides the body (.myio-cgp__grid)
20634
+ and lets the panel shrink to header height. Arrow uses CSS escapes
20635
+ (ASCII only) to stay safe across strict CSS parsers (e.g. ThingsBoard). */
20636
+ .myio-cgp--collapsible > .myio-hp {
20637
+ cursor: pointer;
20638
+ user-select: none;
20639
+ }
20640
+ .myio-cgp--collapsible > .myio-hp .myio-hp__title::before {
20641
+ content: "\\25BE";
20642
+ display: inline-block;
20643
+ margin-right: 6px;
20644
+ font-size: 0.7em;
20645
+ opacity: 0.65;
20646
+ }
20647
+ .myio-cgp--collapsed > .myio-hp .myio-hp__title::before {
20648
+ content: "\\25B8";
20649
+ }
20650
+ .myio-cgp--collapsed > .myio-cgp__grid,
20651
+ .myio-cgp--collapsed > .myio-cgp__tabs-wrapper,
20652
+ .myio-cgp--collapsed > .myio-cgp__content {
20653
+ display: none !important;
20654
+ }
20655
+ .myio-cgp--collapsed {
20656
+ height: auto !important;
20657
+ flex: 0 0 auto !important;
20658
+ }
20630
20659
  `;
20631
20660
  function injectStyles5() {
20632
20661
  if (document.getElementById(CSS_ID3)) return;
@@ -20643,9 +20672,13 @@ var CardGridPanel = class {
20643
20672
  headerComponent = null;
20644
20673
  searchText = "";
20645
20674
  tabsContainer = null;
20675
+ collapsible = false;
20676
+ collapsed = false;
20677
+ headerClickBound = false;
20646
20678
  constructor(options) {
20647
20679
  injectStyles5();
20648
20680
  this.options = options;
20681
+ this.collapsible = !!options.collapsible;
20649
20682
  this.root = document.createElement("div");
20650
20683
  this.root.className = "myio-cgp";
20651
20684
  if (options.panelBackground) {
@@ -20795,6 +20828,46 @@ var CardGridPanel = class {
20795
20828
  this.root.appendChild(grid);
20796
20829
  this.renderTabs();
20797
20830
  this.renderGrid();
20831
+ this.applyCollapsibleState();
20832
+ }
20833
+ // ── Accordion / collapse (opt-in via options.collapsible) ──
20834
+ /** Enable/disable collapsible behavior at runtime. */
20835
+ setCollapsible(value) {
20836
+ this.collapsible = value;
20837
+ if (!value) this.collapsed = false;
20838
+ this.applyCollapsibleState();
20839
+ }
20840
+ /** Collapse or expand the panel body programmatically. */
20841
+ setCollapsed(value) {
20842
+ if (!this.collapsible) return;
20843
+ this.collapsed = value;
20844
+ this.root.classList.toggle("myio-cgp--collapsed", this.collapsed);
20845
+ }
20846
+ /** Toggle collapsed state. */
20847
+ toggleCollapsed() {
20848
+ this.setCollapsed(!this.collapsed);
20849
+ }
20850
+ /** Returns whether the panel is currently collapsed. */
20851
+ isCollapsed() {
20852
+ return this.collapsed;
20853
+ }
20854
+ /** Reflects collapsible/collapsed state on the DOM and wires the header click once. */
20855
+ applyCollapsibleState() {
20856
+ this.root.classList.toggle("myio-cgp--collapsible", this.collapsible);
20857
+ if (!this.collapsible) this.collapsed = false;
20858
+ this.root.classList.toggle("myio-cgp--collapsed", this.collapsible && this.collapsed);
20859
+ if (this.collapsible && !this.headerClickBound) {
20860
+ const header = this.headerComponent?.getElement();
20861
+ if (header) {
20862
+ this.headerClickBound = true;
20863
+ header.addEventListener("click", (e) => {
20864
+ if (!this.collapsible) return;
20865
+ const target = e.target;
20866
+ if (target.closest && target.closest('button, a, input, select, svg, [role="button"]')) return;
20867
+ this.toggleCollapsed();
20868
+ });
20869
+ }
20870
+ }
20798
20871
  }
20799
20872
  renderGrid() {
20800
20873
  const grid = this.root.querySelector(".myio-cgp__grid");
@@ -23767,8 +23840,14 @@ var CDN_RESOURCES = [
23767
23840
  },
23768
23841
  {
23769
23842
  id: "daterangepicker-3.1.0",
23770
- src: "https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js",
23771
- integrity: "sha384-IbJFThFkdkMvvxP0U8wOffxBHPYEJE65UtA/l25/jJQUt/hft6OdAuKLxGjtOVnL",
23843
+ // IMPORTANT: use the ORIGINAL packaged file, NOT daterangepicker.min.js.
23844
+ // The npm package ships no .min.js — jsDelivr auto-minifies on the fly and
23845
+ // that output is NOT byte-stable across edges/minifier versions, so any
23846
+ // pinned SRI hash for the .min.js URL breaks randomly in production
23847
+ // (observed 2026-06-09: same URL served two different digests).
23848
+ // The packaged daterangepicker.js bytes are immutable for a pinned version.
23849
+ src: "https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.js",
23850
+ integrity: "sha384-mnAM7GRdEYmwFIfdpoD/nOPQFbdJrbTrXa7om4CrSzKCQfysFJ0jbwgw+55cdvi7",
23772
23851
  crossorigin: "anonymous"
23773
23852
  }
23774
23853
  ];
@@ -79971,6 +80050,21 @@ function getWidgetStyles(theme, primaryColor, headerStyles) {
79971
80050
  flex-wrap: wrap;
79972
80051
  }
79973
80052
 
80053
+ /* Accordion (opt-in via config.collapsible): header clicavel; recolhido
80054
+ esconde o corpo + footer, deixando so a barra do header. */
80055
+ .myio-chart-widget--collapsible .myio-chart-widget-header {
80056
+ cursor: pointer;
80057
+ user-select: none;
80058
+ }
80059
+ .myio-chart-widget--collapsed .myio-chart-widget-body,
80060
+ .myio-chart-widget--collapsed .myio-chart-widget-footer {
80061
+ display: none !important;
80062
+ }
80063
+ .myio-chart-widget--collapsed {
80064
+ height: auto !important;
80065
+ flex: 0 0 auto !important;
80066
+ }
80067
+
79974
80068
  .myio-chart-widget-stat {
79975
80069
  display: flex;
79976
80070
  flex-direction: column;
@@ -80326,6 +80420,9 @@ function createConsumptionChartWidget(config) {
80326
80420
  let currentVizMode = config.defaultVizMode ?? "total";
80327
80421
  let currentPeriod = config.defaultPeriod ?? 7;
80328
80422
  let currentIdealRange = config.idealRange ?? null;
80423
+ let isCollapsible = !!config.collapsible;
80424
+ let isCollapsed = false;
80425
+ let collapseWired = false;
80329
80426
  let isLoading = false;
80330
80427
  let tempPeriod = currentPeriod;
80331
80428
  let tempChartType = currentChartType;
@@ -81086,6 +81183,26 @@ function createConsumptionChartWidget(config) {
81086
81183
  }
81087
81184
  });
81088
81185
  }
81186
+ function applyCollapseState() {
81187
+ const widget = document.getElementById(widgetId);
81188
+ if (!widget) return;
81189
+ widget.classList.toggle("myio-chart-widget--collapsible", isCollapsible);
81190
+ if (!isCollapsible) isCollapsed = false;
81191
+ widget.classList.toggle("myio-chart-widget--collapsed", isCollapsible && isCollapsed);
81192
+ if (isCollapsible && !collapseWired) {
81193
+ const header = widget.querySelector(".myio-chart-widget-header");
81194
+ if (header) {
81195
+ collapseWired = true;
81196
+ header.addEventListener("click", (e) => {
81197
+ if (!isCollapsible) return;
81198
+ const target = e.target;
81199
+ if (target.closest && target.closest('button, a, input, select, svg, [role="button"], [class*="-tab"]')) return;
81200
+ isCollapsed = !isCollapsed;
81201
+ applyCollapseState();
81202
+ });
81203
+ }
81204
+ }
81205
+ }
81089
81206
  const instance = {
81090
81207
  async render() {
81091
81208
  containerElement = document.getElementById(config.containerId);
@@ -81096,6 +81213,7 @@ function createConsumptionChartWidget(config) {
81096
81213
  injectStyles16();
81097
81214
  containerElement.innerHTML = renderHTML();
81098
81215
  setupListeners();
81216
+ applyCollapseState();
81099
81217
  setLoading(true);
81100
81218
  chartInstance = createConsumption7DaysChart({
81101
81219
  ...config,
@@ -81186,6 +81304,21 @@ function createConsumptionChartWidget(config) {
81186
81304
  exportCSV(filename) {
81187
81305
  chartInstance?.exportCSV(filename);
81188
81306
  },
81307
+ setCollapsible(value) {
81308
+ isCollapsible = value;
81309
+ if (!value) isCollapsed = false;
81310
+ applyCollapseState();
81311
+ },
81312
+ setCollapsed(value) {
81313
+ if (!isCollapsible) return;
81314
+ isCollapsed = value;
81315
+ applyCollapseState();
81316
+ },
81317
+ toggleCollapsed() {
81318
+ if (!isCollapsible) return;
81319
+ isCollapsed = !isCollapsed;
81320
+ applyCollapseState();
81321
+ },
81189
81322
  destroy() {
81190
81323
  chartInstance?.destroy();
81191
81324
  chartInstance = null;
@@ -136314,40 +136447,94 @@ function injectSidebarMenuStyles() {
136314
136447
  background: #6b7280;
136315
136448
  }
136316
136449
 
136317
- /* ===== Responsive ===== */
136318
- @media (max-width: 768px) {
136319
- .${SIDEBAR_MENU_CSS_PREFIX} {
136320
- position: fixed;
136321
- left: 0;
136322
- top: 0;
136323
- height: 100vh;
136324
- transform: translateX(-100%);
136325
- box-shadow: none;
136326
- z-index: 1000;
136327
- }
136450
+ /* ===== Mobile mode (JS-driven via .--mobile, NOT @media) =====
136451
+ The host measures its real element width and calls setMobileMode();
136452
+ in ThingsBoard the widget element width != viewport, so @media is
136453
+ unreliable. In mobile mode the sidebar becomes an off-canvas drawer
136454
+ opened by a persistent top bar (hamburger); the drawer keeps its
136455
+ footer/logout. */
136328
136456
 
136329
- .${SIDEBAR_MENU_CSS_PREFIX}.mobile-open {
136330
- transform: translateX(0);
136331
- box-shadow: 4px 0 20px rgba(0, 0, 0, 0.15);
136332
- }
136457
+ /* Persistent top bar with the hamburger \u2014 hidden until mobile */
136458
+ .${SIDEBAR_MENU_CSS_PREFIX}__mobile-topbar {
136459
+ display: none;
136460
+ }
136461
+ .${SIDEBAR_MENU_CSS_PREFIX}__mobile-topbar.is-mobile {
136462
+ display: flex;
136463
+ align-items: center;
136464
+ gap: 12px;
136465
+ width: 100%;
136466
+ height: 56px;
136467
+ padding: 0 12px;
136468
+ box-sizing: border-box;
136469
+ background: linear-gradient(135deg, var(--sidebar-primary) 0%, var(--sidebar-primary-light) 100%);
136470
+ color: #ffffff;
136471
+ z-index: 900;
136472
+ }
136473
+ .${SIDEBAR_MENU_CSS_PREFIX}__mobile-hamburger {
136474
+ display: inline-flex;
136475
+ align-items: center;
136476
+ justify-content: center;
136477
+ width: 40px;
136478
+ height: 40px;
136479
+ padding: 0;
136480
+ border: none;
136481
+ border-radius: 8px;
136482
+ background: rgba(255, 255, 255, 0.14);
136483
+ color: #ffffff;
136484
+ cursor: pointer;
136485
+ transition: background 0.2s ease;
136486
+ }
136487
+ .${SIDEBAR_MENU_CSS_PREFIX}__mobile-hamburger:hover {
136488
+ background: rgba(255, 255, 255, 0.26);
136489
+ }
136490
+ .${SIDEBAR_MENU_CSS_PREFIX}__mobile-hamburger svg {
136491
+ width: 22px;
136492
+ height: 22px;
136493
+ }
136494
+ .${SIDEBAR_MENU_CSS_PREFIX}__mobile-topbar-title {
136495
+ font-size: 15px;
136496
+ font-weight: 700;
136497
+ letter-spacing: 0.2px;
136498
+ }
136333
136499
 
136334
- .${SIDEBAR_MENU_CSS_PREFIX}__backdrop {
136335
- position: fixed;
136336
- top: 0;
136337
- left: 0;
136338
- right: 0;
136339
- bottom: 0;
136340
- background: rgba(0, 0, 0, 0.5);
136341
- opacity: 0;
136342
- visibility: hidden;
136343
- transition: opacity 0.3s ease, visibility 0.3s ease;
136344
- z-index: 999;
136345
- }
136500
+ /* Sidebar becomes an off-canvas drawer */
136501
+ .${SIDEBAR_MENU_CSS_PREFIX}--mobile {
136502
+ position: fixed;
136503
+ left: 0;
136504
+ top: 0;
136505
+ height: 100vh;
136506
+ width: var(--sidebar-expanded-width);
136507
+ max-width: 86vw;
136508
+ transform: translateX(-100%);
136509
+ box-shadow: none;
136510
+ z-index: 1000;
136511
+ transition: transform var(--sidebar-transition);
136512
+ }
136513
+ .${SIDEBAR_MENU_CSS_PREFIX}--mobile.mobile-open {
136514
+ transform: translateX(0);
136515
+ box-shadow: 4px 0 20px rgba(0, 0, 0, 0.15);
136516
+ }
136346
136517
 
136347
- .${SIDEBAR_MENU_CSS_PREFIX}.mobile-open + .${SIDEBAR_MENU_CSS_PREFIX}__backdrop {
136348
- opacity: 1;
136349
- visibility: visible;
136350
- }
136518
+ /* Backdrop \u2014 only present/active in mobile mode */
136519
+ .${SIDEBAR_MENU_CSS_PREFIX}__backdrop {
136520
+ display: none;
136521
+ position: fixed;
136522
+ top: 0;
136523
+ left: 0;
136524
+ right: 0;
136525
+ bottom: 0;
136526
+ background: rgba(0, 0, 0, 0.5);
136527
+ opacity: 0;
136528
+ visibility: hidden;
136529
+ transition: opacity 0.3s ease, visibility 0.3s ease;
136530
+ z-index: 999;
136531
+ }
136532
+ .${SIDEBAR_MENU_CSS_PREFIX}__backdrop.is-mobile {
136533
+ display: block;
136534
+ }
136535
+ .${SIDEBAR_MENU_CSS_PREFIX}__backdrop.is-open {
136536
+ opacity: 1;
136537
+ visibility: visible;
136351
136538
  }
136352
136539
  `;
136353
136540
  document.head.appendChild(style);
@@ -136424,8 +136611,15 @@ var SidebarMenuView = class {
136424
136611
  activeItemId = null;
136425
136612
  collapsedSections = /* @__PURE__ */ new Set();
136426
136613
  tooltip = null;
136614
+ container;
136615
+ // Mobile mode (JS-driven; NOT @media — the host measures its real width)
136616
+ mobile = false;
136617
+ drawerOpen = false;
136618
+ mobileTopbar = null;
136619
+ backdrop = null;
136427
136620
  constructor(container, config) {
136428
136621
  injectSidebarMenuStyles();
136622
+ this.container = container;
136429
136623
  this.config = { ...DEFAULT_SIDEBAR_CONFIG, ...config };
136430
136624
  this.state = this.config.initialState || "expanded";
136431
136625
  this.themeMode = this.config.themeMode || "light";
@@ -136666,7 +136860,11 @@ var SidebarMenuView = class {
136666
136860
  const target = e.target;
136667
136861
  const action = target.closest("[data-action]")?.getAttribute("data-action");
136668
136862
  if (action === "toggle") {
136669
- this.toggle();
136863
+ if (this.mobile) {
136864
+ this.closeDrawer();
136865
+ } else {
136866
+ this.toggle();
136867
+ }
136670
136868
  } else if (action === "theme-toggle") {
136671
136869
  this.handleThemeToggle();
136672
136870
  } else if (action === "logout") {
@@ -136686,6 +136884,7 @@ var SidebarMenuView = class {
136686
136884
  const sectionId = itemEl.getAttribute("data-section-id");
136687
136885
  if (itemId && sectionId) {
136688
136886
  this.handleItemClick(itemId, sectionId);
136887
+ if (this.mobile) this.closeDrawer();
136689
136888
  }
136690
136889
  }
136691
136890
  }
@@ -136834,6 +137033,71 @@ var SidebarMenuView = class {
136834
137033
  getState() {
136835
137034
  return this.state;
136836
137035
  }
137036
+ /**
137037
+ * Toggle mobile mode. Driven by the host (it measures its real width),
137038
+ * NOT by @media — the widget element width != viewport in ThingsBoard.
137039
+ * In mobile mode the sidebar becomes an off-canvas drawer and a persistent
137040
+ * top bar (hamburger) is shown to open it; the drawer keeps its footer/logout.
137041
+ */
137042
+ setMobileMode(on) {
137043
+ if (this.mobile === on) return;
137044
+ this.mobile = on;
137045
+ if (on) {
137046
+ this.ensureMobileChrome();
137047
+ this.root.classList.add(`${SIDEBAR_MENU_CSS_PREFIX}--mobile`);
137048
+ this.root.classList.remove("collapsed");
137049
+ if (this.mobileTopbar) this.mobileTopbar.classList.add("is-mobile");
137050
+ if (this.backdrop) this.backdrop.classList.add("is-mobile");
137051
+ this.closeDrawer();
137052
+ } else {
137053
+ this.closeDrawer();
137054
+ this.root.classList.remove(`${SIDEBAR_MENU_CSS_PREFIX}--mobile`);
137055
+ if (this.mobileTopbar) this.mobileTopbar.classList.remove("is-mobile");
137056
+ if (this.backdrop) this.backdrop.classList.remove("is-mobile");
137057
+ if (this.state === "collapsed") this.root.classList.add("collapsed");
137058
+ }
137059
+ }
137060
+ /** Lazily create the persistent mobile top bar (hamburger) + backdrop. */
137061
+ ensureMobileChrome() {
137062
+ if (!this.mobileTopbar) {
137063
+ const header = this.config.header || {};
137064
+ const title = header.title || "Menu";
137065
+ const bar = document.createElement("div");
137066
+ bar.className = `${SIDEBAR_MENU_CSS_PREFIX}__mobile-topbar`;
137067
+ bar.innerHTML = `
137068
+ <button
137069
+ class="${SIDEBAR_MENU_CSS_PREFIX}__mobile-hamburger"
137070
+ data-action="mobile-open"
137071
+ aria-label="Abrir menu"
137072
+ >${SIDEBAR_ICONS.menu}</button>
137073
+ <span class="${SIDEBAR_MENU_CSS_PREFIX}__mobile-topbar-title">${title}</span>
137074
+ `;
137075
+ bar.querySelector('[data-action="mobile-open"]')?.addEventListener("click", () => this.openDrawer());
137076
+ this.container.insertBefore(bar, this.root);
137077
+ this.mobileTopbar = bar;
137078
+ }
137079
+ if (!this.backdrop) {
137080
+ const bd = document.createElement("div");
137081
+ bd.className = `${SIDEBAR_MENU_CSS_PREFIX}__backdrop`;
137082
+ bd.addEventListener("click", () => this.closeDrawer());
137083
+ this.container.insertBefore(bd, this.root.nextSibling);
137084
+ this.backdrop = bd;
137085
+ }
137086
+ }
137087
+ openDrawer() {
137088
+ if (!this.mobile) return;
137089
+ this.drawerOpen = true;
137090
+ this.root.classList.add("mobile-open");
137091
+ this.backdrop?.classList.add("is-open");
137092
+ }
137093
+ closeDrawer() {
137094
+ this.drawerOpen = false;
137095
+ this.root.classList.remove("mobile-open");
137096
+ this.backdrop?.classList.remove("is-open");
137097
+ }
137098
+ isMobile() {
137099
+ return this.mobile;
137100
+ }
136837
137101
  setThemeMode(mode) {
136838
137102
  this.themeMode = mode;
136839
137103
  this.root.classList.remove("light", "dark");
@@ -136959,6 +137223,19 @@ var SidebarMenuController = class {
136959
137223
  toggle() {
136960
137224
  this.view.toggle();
136961
137225
  }
137226
+ /** Enable/disable mobile mode (hamburger top bar + off-canvas drawer). */
137227
+ setMobileMode(on) {
137228
+ this.view.setMobileMode(on);
137229
+ }
137230
+ openDrawer() {
137231
+ this.view.openDrawer();
137232
+ }
137233
+ closeDrawer() {
137234
+ this.view.closeDrawer();
137235
+ }
137236
+ isMobile() {
137237
+ return this.view.isMobile();
137238
+ }
136962
137239
  getState() {
136963
137240
  return this.view.getState();
136964
137241
  }
package/dist/index.d.cts CHANGED
@@ -3177,6 +3177,13 @@ interface CardGridPanelOptions {
3177
3177
  tabs?: TabItem[];
3178
3178
  /** Callback when tab selection changes (receives the newly selected tab) */
3179
3179
  onTabChange?: (tab: TabItem) => void;
3180
+ /**
3181
+ * When true, the panel becomes an accordion item: clicking the header
3182
+ * (anywhere except its buttons) collapses/expands the body. A small arrow
3183
+ * indicator is shown before the title. Default: false (fully backward-compatible).
3184
+ * Can be toggled at runtime via setCollapsible().
3185
+ */
3186
+ collapsible?: boolean;
3180
3187
  }
3181
3188
  declare class CardGridPanel {
3182
3189
  private root;
@@ -3184,6 +3191,9 @@ declare class CardGridPanel {
3184
3191
  private headerComponent;
3185
3192
  private searchText;
3186
3193
  private tabsContainer;
3194
+ private collapsible;
3195
+ private collapsed;
3196
+ private headerClickBound;
3187
3197
  constructor(options: CardGridPanelOptions);
3188
3198
  getElement(): HTMLElement;
3189
3199
  /** Replace items and re-render grid */
@@ -3210,6 +3220,16 @@ declare class CardGridPanel {
3210
3220
  setCardCustomStyle(style: CardGridCustomStyle | undefined): void;
3211
3221
  destroy(): void;
3212
3222
  private render;
3223
+ /** Enable/disable collapsible behavior at runtime. */
3224
+ setCollapsible(value: boolean): void;
3225
+ /** Collapse or expand the panel body programmatically. */
3226
+ setCollapsed(value: boolean): void;
3227
+ /** Toggle collapsed state. */
3228
+ toggleCollapsed(): void;
3229
+ /** Returns whether the panel is currently collapsed. */
3230
+ isCollapsed(): boolean;
3231
+ /** Reflects collapsible/collapsed state on the DOM and wires the header click once. */
3232
+ private applyCollapsibleState;
3213
3233
  private renderGrid;
3214
3234
  private renderTabs;
3215
3235
  }
@@ -9518,6 +9538,12 @@ interface ConsumptionWidgetConfig extends Omit<Consumption7DaysConfig, 'containe
9518
9538
  onMaximizeClick?: () => void;
9519
9539
  /** Custom CSS class for the widget container */
9520
9540
  className?: string;
9541
+ /**
9542
+ * When true, the widget becomes an accordion item: clicking the header
9543
+ * (except its buttons/tabs) collapses/expands the chart body, leaving only
9544
+ * the header bar. Default: false. Toggle at runtime via setCollapsible().
9545
+ */
9546
+ collapsible?: boolean;
9521
9547
  /**
9522
9548
  * Custom header styles for compact layouts
9523
9549
  * @example { padding: '8px 12px', fontSize: '12px', titleFontSize: '12px', tabPadding: '4px 10px', tabFontSize: '11px' }
@@ -9566,6 +9592,12 @@ interface ConsumptionWidgetInstance {
9566
9592
  };
9567
9593
  /** Exports data to CSV */
9568
9594
  exportCSV: (filename?: string) => void;
9595
+ /** Enable/disable collapsible (accordion) behavior at runtime. */
9596
+ setCollapsible: (value: boolean) => void;
9597
+ /** Collapse/expand the chart body, keeping only the header bar. */
9598
+ setCollapsed: (value: boolean) => void;
9599
+ /** Toggle collapsed state. */
9600
+ toggleCollapsed: () => void;
9569
9601
  /** Destroys the widget */
9570
9602
  destroy: () => void;
9571
9603
  }
@@ -18445,6 +18477,11 @@ declare class SidebarMenuController implements SidebarMenuInstance {
18445
18477
  expand(): void;
18446
18478
  collapse(): void;
18447
18479
  toggle(): void;
18480
+ /** Enable/disable mobile mode (hamburger top bar + off-canvas drawer). */
18481
+ setMobileMode(on: boolean): void;
18482
+ openDrawer(): void;
18483
+ closeDrawer(): void;
18484
+ isMobile(): boolean;
18448
18485
  getState(): SidebarState;
18449
18486
  setThemeMode(mode: SidebarThemeMode): void;
18450
18487
  updateSections(sections: SidebarMenuSection[]): void;
@@ -18473,6 +18510,11 @@ declare class SidebarMenuView {
18473
18510
  private activeItemId;
18474
18511
  private collapsedSections;
18475
18512
  private tooltip;
18513
+ private container;
18514
+ private mobile;
18515
+ private drawerOpen;
18516
+ private mobileTopbar;
18517
+ private backdrop;
18476
18518
  constructor(container: HTMLElement, config: SidebarMenuConfig);
18477
18519
  private createSidebar;
18478
18520
  private renderHeader;
@@ -18493,6 +18535,18 @@ declare class SidebarMenuView {
18493
18535
  collapse(): void;
18494
18536
  toggle(): void;
18495
18537
  getState(): SidebarState;
18538
+ /**
18539
+ * Toggle mobile mode. Driven by the host (it measures its real width),
18540
+ * NOT by @media — the widget element width != viewport in ThingsBoard.
18541
+ * In mobile mode the sidebar becomes an off-canvas drawer and a persistent
18542
+ * top bar (hamburger) is shown to open it; the drawer keeps its footer/logout.
18543
+ */
18544
+ setMobileMode(on: boolean): void;
18545
+ /** Lazily create the persistent mobile top bar (hamburger) + backdrop. */
18546
+ private ensureMobileChrome;
18547
+ openDrawer(): void;
18548
+ closeDrawer(): void;
18549
+ isMobile(): boolean;
18496
18550
  setThemeMode(mode: SidebarThemeMode): void;
18497
18551
  updateSections(sections: SidebarMenuSection[]): void;
18498
18552
  updateItemBadge(itemId: string, badge: number | string | null): void;