myio-js-library 0.1.498 → 0.1.499

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
@@ -1162,7 +1162,7 @@ module.exports = __toCommonJS(index_exports);
1162
1162
  // package.json
1163
1163
  var package_default = {
1164
1164
  name: "myio-js-library",
1165
- version: "0.1.498",
1165
+ version: "0.1.499",
1166
1166
  description: "A clean, standalone JS SDK for MYIO projects",
1167
1167
  license: "MIT",
1168
1168
  repository: "github:gh-myio/myio-js-library",
@@ -5548,7 +5548,7 @@ var MyIOSelectionStoreClass = class _MyIOSelectionStoreClass {
5548
5548
  if (_MyIOSelectionStoreClass.GlobalDebug) {
5549
5549
  console.trace("[SelectionStore] Constructor called from:");
5550
5550
  }
5551
- this.MAX_SELECTION = 6;
5551
+ this.MAX_SELECTION = 20;
5552
5552
  this.state = { selectedDevice: null };
5553
5553
  this.selectedIds = /* @__PURE__ */ new Set();
5554
5554
  this.entities = /* @__PURE__ */ new Map();
@@ -5633,6 +5633,45 @@ var MyIOSelectionStoreClass = class _MyIOSelectionStoreClass {
5633
5633
  this._emitSelectionChange("clear");
5634
5634
  this._trackEvent("footer_dock.clear_all", { count: 0 });
5635
5635
  }
5636
+ /**
5637
+ * Updates the maximum number of concurrent selections at runtime.
5638
+ * Typically called once from MAIN_VIEW onInit with the value from widget
5639
+ * settings so operators can configure it per-customer without code changes.
5640
+ *
5641
+ * If the current selection already exceeds the new limit, keeps the first N
5642
+ * in insertion order, drops the rest, and emits `selection:change` plus
5643
+ * `selection:limit-reached` so any UI reflects the truncation.
5644
+ *
5645
+ * @param {number} n - new maximum (integer ≥ 1)
5646
+ */
5647
+ setMaxSelection(n) {
5648
+ const v = Math.floor(Number(n));
5649
+ if (!Number.isFinite(v) || v < 1) {
5650
+ this._log("warn", `[MyIOSelectionStoreClass] Invalid maxSelection, ignoring:`, n);
5651
+ return;
5652
+ }
5653
+ if (v === this.MAX_SELECTION) return;
5654
+ const prev = this.MAX_SELECTION;
5655
+ this.MAX_SELECTION = v;
5656
+ this._log("log", `[MyIOSelectionStoreClass] MAX_SELECTION ${prev} \u2192 ${v}`);
5657
+ if (this.selectedIds.size > v) {
5658
+ const kept = Array.from(this.selectedIds).slice(0, v);
5659
+ this.selectedIds = new Set(kept);
5660
+ this._emitSelectionChange("truncate");
5661
+ this._emit("selection:limit-reached", {
5662
+ maxAllowed: v,
5663
+ currentCount: this.selectedIds.size,
5664
+ truncated: true
5665
+ });
5666
+ }
5667
+ }
5668
+ /**
5669
+ * Returns the currently active selection limit.
5670
+ * @returns {number}
5671
+ */
5672
+ getMaxSelection() {
5673
+ return this.MAX_SELECTION;
5674
+ }
5636
5675
  syncFromCheckbox(id, checked) {
5637
5676
  if (typeof id !== "string" || typeof checked !== "boolean") return;
5638
5677
  if (checked && !this.isSelected(id)) {
@@ -30587,27 +30626,36 @@ var EnergyModalView = class {
30587
30626
  }
30588
30627
  /**
30589
30628
  * (Re)builds the DateRangePicker. Time picker only shown when granularity = '1h'.
30590
- * Preserves the currently selected range when rebuilding after a granularity change.
30629
+ *
30630
+ * Preserves only the DATE portion of the previous range — times are always
30631
+ * reset to 00:00:00 (start) / 23:59:59 (end) on every rebuild, so toggling
30632
+ * the granularity tab reliably brings back the full-day default. In 1h
30633
+ * mode the user can then adjust hours/minutes freely; switching back to
30634
+ * 1d wipes any custom time and restores the full-day boundaries.
30591
30635
  */
30592
30636
  async rebuildDateRangePicker(input) {
30593
- let presetStart;
30594
- let presetEnd;
30637
+ const toYmd = (v) => {
30638
+ if (!v) return void 0;
30639
+ if (v instanceof Date) return v.toISOString().split("T")[0];
30640
+ if (typeof v === "string") return v.split("T")[0];
30641
+ return void 0;
30642
+ };
30643
+ let startYmd;
30644
+ let endYmd;
30595
30645
  if (this.dateRangePicker) {
30596
30646
  try {
30597
30647
  const current = this.dateRangePicker.getDates();
30598
- presetStart = current.startISO;
30599
- presetEnd = current.endISO;
30648
+ startYmd = toYmd(current.startISO);
30649
+ endYmd = toYmd(current.endISO);
30600
30650
  } catch {
30601
30651
  }
30602
30652
  this.dateRangePicker.destroy();
30603
30653
  this.dateRangePicker = null;
30604
30654
  }
30605
- if (!presetStart) {
30606
- presetStart = this.config.params.startDate instanceof Date ? this.config.params.startDate.toISOString().split("T")[0] : this.config.params.startDate;
30607
- }
30608
- if (!presetEnd) {
30609
- presetEnd = this.config.params.endDate instanceof Date ? this.config.params.endDate.toISOString().split("T")[0] : this.config.params.endDate;
30610
- }
30655
+ if (!startYmd) startYmd = toYmd(this.config.params.startDate);
30656
+ if (!endYmd) endYmd = toYmd(this.config.params.endDate);
30657
+ const presetStart = startYmd ? `${startYmd}T00:00:00-03:00` : void 0;
30658
+ const presetEnd = endYmd ? `${endYmd}T23:59:59-03:00` : void 0;
30611
30659
  try {
30612
30660
  this.dateRangePicker = await attach(input, {
30613
30661
  presetStart,
@@ -31631,20 +31679,28 @@ var DeviceReportModal = class {
31631
31679
  * Preserves the currently selected range when rebuilding after a granularity change.
31632
31680
  */
31633
31681
  async rebuildDateRangePicker(input) {
31634
- let presetStart;
31635
- let presetEnd;
31682
+ const toYmd = (v) => {
31683
+ if (!v) return void 0;
31684
+ if (v instanceof Date) return v.toISOString().split("T")[0];
31685
+ if (typeof v === "string") return v.split("T")[0];
31686
+ return void 0;
31687
+ };
31688
+ let startYmd;
31689
+ let endYmd;
31636
31690
  if (this.dateRangePicker) {
31637
31691
  try {
31638
31692
  const current = this.dateRangePicker.getDates();
31639
- presetStart = current.startISO;
31640
- presetEnd = current.endISO;
31693
+ startYmd = toYmd(current.startISO);
31694
+ endYmd = toYmd(current.endISO);
31641
31695
  } catch {
31642
31696
  }
31643
31697
  this.dateRangePicker.destroy();
31644
31698
  this.dateRangePicker = null;
31645
31699
  }
31646
- if (!presetStart) presetStart = this.getDefaultStartDate();
31647
- if (!presetEnd) presetEnd = this.getDefaultEndDate();
31700
+ if (!startYmd) startYmd = toYmd(this.getDefaultStartDate());
31701
+ if (!endYmd) endYmd = toYmd(this.getDefaultEndDate());
31702
+ const presetStart = startYmd ? `${startYmd}T00:00:00-03:00` : void 0;
31703
+ const presetEnd = endYmd ? `${endYmd}T23:59:59-03:00` : void 0;
31648
31704
  try {
31649
31705
  this.dateRangePicker = await attach(input, {
31650
31706
  presetStart,
@@ -41746,7 +41802,7 @@ var DEFAULT_CONFIG_TEMPLATE2 = {
41746
41802
  emptyMessage: "Selecione dispositivos para comparar",
41747
41803
  compareButtonLabel: "Comparar",
41748
41804
  clearButtonLabel: "Limpar",
41749
- maxSelections: 6,
41805
+ maxSelections: 20,
41750
41806
  darkMode: DEFAULT_DARK_THEME2,
41751
41807
  lightMode: DEFAULT_LIGHT_THEME2
41752
41808
  };
@@ -43962,9 +44018,28 @@ var InternalSelectionStore = class {
43962
44018
  entities = /* @__PURE__ */ new Map();
43963
44019
  listeners = /* @__PURE__ */ new Map();
43964
44020
  maxSelections;
43965
- constructor(maxSelections = 6) {
44021
+ constructor(maxSelections = DEFAULT_CONFIG_TEMPLATE2.maxSelections ?? 20) {
43966
44022
  this.maxSelections = maxSelections;
43967
44023
  }
44024
+ /**
44025
+ * Updates the maximum selection limit at runtime.
44026
+ * If the current selection exceeds the new limit, trims the oldest
44027
+ * entries (insertion order) and emits `selection:change`.
44028
+ */
44029
+ setMaxSelections(n) {
44030
+ const v = Math.floor(Number(n));
44031
+ if (!Number.isFinite(v) || v < 1) return;
44032
+ this.maxSelections = v;
44033
+ if (this.entities.size > v) {
44034
+ const kept = Array.from(this.entities.entries()).slice(0, v);
44035
+ this.entities = new Map(kept);
44036
+ this.emit("selection:change", this.getSelectedEntities());
44037
+ this.emit("selection:limit-reached", v);
44038
+ }
44039
+ }
44040
+ getMaxSelections() {
44041
+ return this.maxSelections;
44042
+ }
43968
44043
  on(event, handler) {
43969
44044
  if (!this.listeners.has(event)) {
43970
44045
  this.listeners.set(event, []);
@@ -44017,7 +44092,7 @@ var FooterController = class {
44017
44092
  constructor(params, view) {
44018
44093
  this.params = params;
44019
44094
  this.view = view;
44020
- this.maxSelections = params.maxSelections ?? 6;
44095
+ this.maxSelections = params.maxSelections ?? params.configTemplate?.maxSelections ?? DEFAULT_CONFIG_TEMPLATE2.maxSelections ?? 20;
44021
44096
  this.log = createLogger2(params.debug ?? false);
44022
44097
  if (params.colors) {
44023
44098
  setAlertColors(params.colors);
@@ -44361,6 +44436,38 @@ var FooterController = class {
44361
44436
  hideAlert() {
44362
44437
  hideAlert();
44363
44438
  }
44439
+ /**
44440
+ * Updates the maximum selection limit at runtime. If the footer is using
44441
+ * an InternalSelectionStore, the store's limit is updated too; external
44442
+ * stores are left untouched (they own their own limit).
44443
+ *
44444
+ * If the current selection exceeds the new limit, the underlying store
44445
+ * will emit `selection:limit-reached` (and may truncate), and the view
44446
+ * re-renders via the existing selection:change handler.
44447
+ */
44448
+ setMaxSelections(n) {
44449
+ const v = Math.floor(Number(n));
44450
+ if (!Number.isFinite(v) || v < 1) {
44451
+ this.log.warn("setMaxSelections: invalid value, ignoring:", n);
44452
+ return;
44453
+ }
44454
+ this.maxSelections = v;
44455
+ if (this.store instanceof InternalSelectionStore) {
44456
+ this.store.setMaxSelections(v);
44457
+ } else {
44458
+ this.log.log(
44459
+ "setMaxSelections: external store detected \u2014 controller limit updated to",
44460
+ v,
44461
+ "but external store limit is not managed here. Call its own setter if supported."
44462
+ );
44463
+ }
44464
+ }
44465
+ /**
44466
+ * Returns the maximum selection limit currently enforced by the controller.
44467
+ */
44468
+ getMaxSelections() {
44469
+ return this.maxSelections;
44470
+ }
44364
44471
  /**
44365
44472
  * Cleanup resources
44366
44473
  */
@@ -44396,6 +44503,8 @@ function createFooterComponent(params) {
44396
44503
  clearSelection: () => controller.clearSelection(),
44397
44504
  getSelectedEntities: () => controller.getSelectedEntities(),
44398
44505
  getSelectionCount: () => controller.getSelectionCount(),
44506
+ setMaxSelections: (n) => controller.setMaxSelections(n),
44507
+ getMaxSelections: () => controller.getMaxSelections(),
44399
44508
  // State methods
44400
44509
  getCurrentUnitType: () => controller.getCurrentUnitType(),
44401
44510
  setDateRange: (start, end) => controller.setDateRange(start, end),
package/dist/index.d.cts CHANGED
@@ -3090,6 +3090,23 @@ declare class MyIOSelectionStoreClass {
3090
3090
  remove(id: any): void;
3091
3091
  toggle(id: any): void;
3092
3092
  clear(): void;
3093
+ /**
3094
+ * Updates the maximum number of concurrent selections at runtime.
3095
+ * Typically called once from MAIN_VIEW onInit with the value from widget
3096
+ * settings so operators can configure it per-customer without code changes.
3097
+ *
3098
+ * If the current selection already exceeds the new limit, keeps the first N
3099
+ * in insertion order, drops the rest, and emits `selection:change` plus
3100
+ * `selection:limit-reached` so any UI reflects the truncation.
3101
+ *
3102
+ * @param {number} n - new maximum (integer ≥ 1)
3103
+ */
3104
+ setMaxSelection(n: number): void;
3105
+ /**
3106
+ * Returns the currently active selection limit.
3107
+ * @returns {number}
3108
+ */
3109
+ getMaxSelection(): number;
3093
3110
  syncFromCheckbox(id: any, checked: any): void;
3094
3111
  registerEntity(entity: any): void;
3095
3112
  unregisterEntity(id: any): void;
@@ -5499,6 +5516,10 @@ interface SelectionStore {
5499
5516
  clear: () => void;
5500
5517
  getSelectedEntities: () => SelectedEntity[];
5501
5518
  getSelectionCount: () => number;
5519
+ /** Optional runtime setter — present on InternalSelectionStore and on
5520
+ * MyIOSelectionStore (when exposed). Callers should feature-detect. */
5521
+ setMaxSelections?: (n: number) => void;
5522
+ getMaxSelections?: () => number;
5502
5523
  }
5503
5524
  /**
5504
5525
  * Date range for comparison modals
@@ -5523,7 +5544,8 @@ interface FooterComponentParams {
5523
5544
  configTemplate?: FooterConfigTemplate;
5524
5545
  /** Selection store instance (default: window.MyIOSelectionStore) */
5525
5546
  selectionStore?: SelectionStore;
5526
- /** Maximum number of selections allowed (default: 6) */
5547
+ /** Maximum number of selections allowed.
5548
+ * Resolution order: this param > configTemplate.maxSelections > DEFAULT_CONFIG_TEMPLATE.maxSelections (20). */
5527
5549
  maxSelections?: number;
5528
5550
  /** Message shown when dock is empty */
5529
5551
  emptyMessage?: string;
@@ -5585,6 +5607,20 @@ interface FooterComponentInstance {
5585
5607
  * Get the count of selected entities
5586
5608
  */
5587
5609
  getSelectionCount: () => number;
5610
+ /**
5611
+ * Update the maximum number of selections at runtime.
5612
+ * If the current selection exceeds the new limit, it will be truncated
5613
+ * (insertion order) and `selection:limit-reached` will be emitted.
5614
+ *
5615
+ * Note: when the footer is wired to an external store that does not
5616
+ * implement `setMaxSelections`, the controller's internal limit is
5617
+ * updated but the external store's own limit is left untouched.
5618
+ */
5619
+ setMaxSelections: (n: number) => void;
5620
+ /**
5621
+ * Get the maximum number of selections currently enforced.
5622
+ */
5623
+ getMaxSelections: () => number;
5588
5624
  /**
5589
5625
  * Get the current unit type (energy, water, tank, temperature, or null)
5590
5626
  */
@@ -5640,7 +5676,7 @@ interface FooterComponentInstance {
5640
5676
  * const footer = createFooterComponent({
5641
5677
  * container: document.getElementById('footer-container'),
5642
5678
  * theme: 'dark',
5643
- * maxSelections: 6,
5679
+ * maxSelections: 20,
5644
5680
  * onCompareClick: (entities, unitType) => {
5645
5681
  * console.log('Compare clicked:', entities.length, unitType);
5646
5682
  * },
package/dist/index.js CHANGED
@@ -546,7 +546,7 @@ var init_template_card = __esm({
546
546
  // package.json
547
547
  var package_default = {
548
548
  name: "myio-js-library",
549
- version: "0.1.498",
549
+ version: "0.1.499",
550
550
  description: "A clean, standalone JS SDK for MYIO projects",
551
551
  license: "MIT",
552
552
  repository: "github:gh-myio/myio-js-library",
@@ -4932,7 +4932,7 @@ var MyIOSelectionStoreClass = class _MyIOSelectionStoreClass {
4932
4932
  if (_MyIOSelectionStoreClass.GlobalDebug) {
4933
4933
  console.trace("[SelectionStore] Constructor called from:");
4934
4934
  }
4935
- this.MAX_SELECTION = 6;
4935
+ this.MAX_SELECTION = 20;
4936
4936
  this.state = { selectedDevice: null };
4937
4937
  this.selectedIds = /* @__PURE__ */ new Set();
4938
4938
  this.entities = /* @__PURE__ */ new Map();
@@ -5017,6 +5017,45 @@ var MyIOSelectionStoreClass = class _MyIOSelectionStoreClass {
5017
5017
  this._emitSelectionChange("clear");
5018
5018
  this._trackEvent("footer_dock.clear_all", { count: 0 });
5019
5019
  }
5020
+ /**
5021
+ * Updates the maximum number of concurrent selections at runtime.
5022
+ * Typically called once from MAIN_VIEW onInit with the value from widget
5023
+ * settings so operators can configure it per-customer without code changes.
5024
+ *
5025
+ * If the current selection already exceeds the new limit, keeps the first N
5026
+ * in insertion order, drops the rest, and emits `selection:change` plus
5027
+ * `selection:limit-reached` so any UI reflects the truncation.
5028
+ *
5029
+ * @param {number} n - new maximum (integer ≥ 1)
5030
+ */
5031
+ setMaxSelection(n) {
5032
+ const v = Math.floor(Number(n));
5033
+ if (!Number.isFinite(v) || v < 1) {
5034
+ this._log("warn", `[MyIOSelectionStoreClass] Invalid maxSelection, ignoring:`, n);
5035
+ return;
5036
+ }
5037
+ if (v === this.MAX_SELECTION) return;
5038
+ const prev = this.MAX_SELECTION;
5039
+ this.MAX_SELECTION = v;
5040
+ this._log("log", `[MyIOSelectionStoreClass] MAX_SELECTION ${prev} \u2192 ${v}`);
5041
+ if (this.selectedIds.size > v) {
5042
+ const kept = Array.from(this.selectedIds).slice(0, v);
5043
+ this.selectedIds = new Set(kept);
5044
+ this._emitSelectionChange("truncate");
5045
+ this._emit("selection:limit-reached", {
5046
+ maxAllowed: v,
5047
+ currentCount: this.selectedIds.size,
5048
+ truncated: true
5049
+ });
5050
+ }
5051
+ }
5052
+ /**
5053
+ * Returns the currently active selection limit.
5054
+ * @returns {number}
5055
+ */
5056
+ getMaxSelection() {
5057
+ return this.MAX_SELECTION;
5058
+ }
5020
5059
  syncFromCheckbox(id, checked) {
5021
5060
  if (typeof id !== "string" || typeof checked !== "boolean") return;
5022
5061
  if (checked && !this.isSelected(id)) {
@@ -29971,27 +30010,36 @@ var EnergyModalView = class {
29971
30010
  }
29972
30011
  /**
29973
30012
  * (Re)builds the DateRangePicker. Time picker only shown when granularity = '1h'.
29974
- * Preserves the currently selected range when rebuilding after a granularity change.
30013
+ *
30014
+ * Preserves only the DATE portion of the previous range — times are always
30015
+ * reset to 00:00:00 (start) / 23:59:59 (end) on every rebuild, so toggling
30016
+ * the granularity tab reliably brings back the full-day default. In 1h
30017
+ * mode the user can then adjust hours/minutes freely; switching back to
30018
+ * 1d wipes any custom time and restores the full-day boundaries.
29975
30019
  */
29976
30020
  async rebuildDateRangePicker(input) {
29977
- let presetStart;
29978
- let presetEnd;
30021
+ const toYmd = (v) => {
30022
+ if (!v) return void 0;
30023
+ if (v instanceof Date) return v.toISOString().split("T")[0];
30024
+ if (typeof v === "string") return v.split("T")[0];
30025
+ return void 0;
30026
+ };
30027
+ let startYmd;
30028
+ let endYmd;
29979
30029
  if (this.dateRangePicker) {
29980
30030
  try {
29981
30031
  const current = this.dateRangePicker.getDates();
29982
- presetStart = current.startISO;
29983
- presetEnd = current.endISO;
30032
+ startYmd = toYmd(current.startISO);
30033
+ endYmd = toYmd(current.endISO);
29984
30034
  } catch {
29985
30035
  }
29986
30036
  this.dateRangePicker.destroy();
29987
30037
  this.dateRangePicker = null;
29988
30038
  }
29989
- if (!presetStart) {
29990
- presetStart = this.config.params.startDate instanceof Date ? this.config.params.startDate.toISOString().split("T")[0] : this.config.params.startDate;
29991
- }
29992
- if (!presetEnd) {
29993
- presetEnd = this.config.params.endDate instanceof Date ? this.config.params.endDate.toISOString().split("T")[0] : this.config.params.endDate;
29994
- }
30039
+ if (!startYmd) startYmd = toYmd(this.config.params.startDate);
30040
+ if (!endYmd) endYmd = toYmd(this.config.params.endDate);
30041
+ const presetStart = startYmd ? `${startYmd}T00:00:00-03:00` : void 0;
30042
+ const presetEnd = endYmd ? `${endYmd}T23:59:59-03:00` : void 0;
29995
30043
  try {
29996
30044
  this.dateRangePicker = await attach(input, {
29997
30045
  presetStart,
@@ -31015,20 +31063,28 @@ var DeviceReportModal = class {
31015
31063
  * Preserves the currently selected range when rebuilding after a granularity change.
31016
31064
  */
31017
31065
  async rebuildDateRangePicker(input) {
31018
- let presetStart;
31019
- let presetEnd;
31066
+ const toYmd = (v) => {
31067
+ if (!v) return void 0;
31068
+ if (v instanceof Date) return v.toISOString().split("T")[0];
31069
+ if (typeof v === "string") return v.split("T")[0];
31070
+ return void 0;
31071
+ };
31072
+ let startYmd;
31073
+ let endYmd;
31020
31074
  if (this.dateRangePicker) {
31021
31075
  try {
31022
31076
  const current = this.dateRangePicker.getDates();
31023
- presetStart = current.startISO;
31024
- presetEnd = current.endISO;
31077
+ startYmd = toYmd(current.startISO);
31078
+ endYmd = toYmd(current.endISO);
31025
31079
  } catch {
31026
31080
  }
31027
31081
  this.dateRangePicker.destroy();
31028
31082
  this.dateRangePicker = null;
31029
31083
  }
31030
- if (!presetStart) presetStart = this.getDefaultStartDate();
31031
- if (!presetEnd) presetEnd = this.getDefaultEndDate();
31084
+ if (!startYmd) startYmd = toYmd(this.getDefaultStartDate());
31085
+ if (!endYmd) endYmd = toYmd(this.getDefaultEndDate());
31086
+ const presetStart = startYmd ? `${startYmd}T00:00:00-03:00` : void 0;
31087
+ const presetEnd = endYmd ? `${endYmd}T23:59:59-03:00` : void 0;
31032
31088
  try {
31033
31089
  this.dateRangePicker = await attach(input, {
31034
31090
  presetStart,
@@ -41130,7 +41186,7 @@ var DEFAULT_CONFIG_TEMPLATE2 = {
41130
41186
  emptyMessage: "Selecione dispositivos para comparar",
41131
41187
  compareButtonLabel: "Comparar",
41132
41188
  clearButtonLabel: "Limpar",
41133
- maxSelections: 6,
41189
+ maxSelections: 20,
41134
41190
  darkMode: DEFAULT_DARK_THEME2,
41135
41191
  lightMode: DEFAULT_LIGHT_THEME2
41136
41192
  };
@@ -43346,9 +43402,28 @@ var InternalSelectionStore = class {
43346
43402
  entities = /* @__PURE__ */ new Map();
43347
43403
  listeners = /* @__PURE__ */ new Map();
43348
43404
  maxSelections;
43349
- constructor(maxSelections = 6) {
43405
+ constructor(maxSelections = DEFAULT_CONFIG_TEMPLATE2.maxSelections ?? 20) {
43350
43406
  this.maxSelections = maxSelections;
43351
43407
  }
43408
+ /**
43409
+ * Updates the maximum selection limit at runtime.
43410
+ * If the current selection exceeds the new limit, trims the oldest
43411
+ * entries (insertion order) and emits `selection:change`.
43412
+ */
43413
+ setMaxSelections(n) {
43414
+ const v = Math.floor(Number(n));
43415
+ if (!Number.isFinite(v) || v < 1) return;
43416
+ this.maxSelections = v;
43417
+ if (this.entities.size > v) {
43418
+ const kept = Array.from(this.entities.entries()).slice(0, v);
43419
+ this.entities = new Map(kept);
43420
+ this.emit("selection:change", this.getSelectedEntities());
43421
+ this.emit("selection:limit-reached", v);
43422
+ }
43423
+ }
43424
+ getMaxSelections() {
43425
+ return this.maxSelections;
43426
+ }
43352
43427
  on(event, handler) {
43353
43428
  if (!this.listeners.has(event)) {
43354
43429
  this.listeners.set(event, []);
@@ -43401,7 +43476,7 @@ var FooterController = class {
43401
43476
  constructor(params, view) {
43402
43477
  this.params = params;
43403
43478
  this.view = view;
43404
- this.maxSelections = params.maxSelections ?? 6;
43479
+ this.maxSelections = params.maxSelections ?? params.configTemplate?.maxSelections ?? DEFAULT_CONFIG_TEMPLATE2.maxSelections ?? 20;
43405
43480
  this.log = createLogger2(params.debug ?? false);
43406
43481
  if (params.colors) {
43407
43482
  setAlertColors(params.colors);
@@ -43745,6 +43820,38 @@ var FooterController = class {
43745
43820
  hideAlert() {
43746
43821
  hideAlert();
43747
43822
  }
43823
+ /**
43824
+ * Updates the maximum selection limit at runtime. If the footer is using
43825
+ * an InternalSelectionStore, the store's limit is updated too; external
43826
+ * stores are left untouched (they own their own limit).
43827
+ *
43828
+ * If the current selection exceeds the new limit, the underlying store
43829
+ * will emit `selection:limit-reached` (and may truncate), and the view
43830
+ * re-renders via the existing selection:change handler.
43831
+ */
43832
+ setMaxSelections(n) {
43833
+ const v = Math.floor(Number(n));
43834
+ if (!Number.isFinite(v) || v < 1) {
43835
+ this.log.warn("setMaxSelections: invalid value, ignoring:", n);
43836
+ return;
43837
+ }
43838
+ this.maxSelections = v;
43839
+ if (this.store instanceof InternalSelectionStore) {
43840
+ this.store.setMaxSelections(v);
43841
+ } else {
43842
+ this.log.log(
43843
+ "setMaxSelections: external store detected \u2014 controller limit updated to",
43844
+ v,
43845
+ "but external store limit is not managed here. Call its own setter if supported."
43846
+ );
43847
+ }
43848
+ }
43849
+ /**
43850
+ * Returns the maximum selection limit currently enforced by the controller.
43851
+ */
43852
+ getMaxSelections() {
43853
+ return this.maxSelections;
43854
+ }
43748
43855
  /**
43749
43856
  * Cleanup resources
43750
43857
  */
@@ -43780,6 +43887,8 @@ function createFooterComponent(params) {
43780
43887
  clearSelection: () => controller.clearSelection(),
43781
43888
  getSelectedEntities: () => controller.getSelectedEntities(),
43782
43889
  getSelectionCount: () => controller.getSelectionCount(),
43890
+ setMaxSelections: (n) => controller.setMaxSelections(n),
43891
+ getMaxSelections: () => controller.getMaxSelections(),
43783
43892
  // State methods
43784
43893
  getCurrentUnitType: () => controller.getCurrentUnitType(),
43785
43894
  setDateRange: (start, end) => controller.setDateRange(start, end),