@tenorlab/react-dashboard 1.4.1 → 1.4.3

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/core.d.ts CHANGED
@@ -5,7 +5,7 @@ export declare const blankDashboardConfig: IDashboardConfig;
5
5
  * Helper function to create dynamic entries
6
6
  * This helps keep the catalog registration clean
7
7
  */
8
- export declare const createDynamicEntry: (key: string, loader: TWidgetFactoryBase, metaData: TWidgetMetaInfoBase) => [string, IDynamicWidgetCatalogEntryBase];
8
+ export declare const createDynamicEntry: (key: string, loader: TWidgetFactoryBase, isRemote: boolean, metaData: TWidgetMetaInfoBase) => [string, IDynamicWidgetCatalogEntryBase];
9
9
 
10
10
  /**
11
11
  * @name createStaticEntry
@@ -14,6 +14,12 @@ export declare const createDynamicEntry: (key: string, loader: TWidgetFactoryBas
14
14
  */
15
15
  export declare const createStaticEntry: <TFrameworkComponentType = any>(key: string, component: TFrameworkComponentType, metaData?: TWidgetMetaInfoBase) => [string, IDynamicWidgetCatalogEntryBase];
16
16
 
17
+ /**
18
+ * @name cssSettingsCatalog
19
+ * @description Catalog of available dashboard settings (array of IDashboardSettingEntry)
20
+ * @see IDashboardSettingEntry
21
+ * @remarks step should never be less than 0.1 as we do some rounding in other places ...
22
+ */
17
23
  export declare const cssSettingsCatalog: IDashboardSettingEntry[];
18
24
 
19
25
  /**
@@ -49,7 +55,18 @@ export declare const DashboardMaxZoomScale: 1;
49
55
 
50
56
  export declare const DashboardMinZoomScale: 0.7;
51
57
 
58
+ /**
59
+ * @name dashboardSettingsUtils
60
+ * @description Contains utils for the dashboard custom settings
61
+ */
52
62
  export declare const dashboardSettingsUtils: {
63
+ /**
64
+ * @name incrementOrDecrementValue
65
+ * @description Increments or decrement a value based on the direction parameter
66
+ * @param item: an instance of IDashboardSettingEntry
67
+ * @param direction: -1 (for decrement) or 1 (for increment)
68
+ * @returns the update item
69
+ */
53
70
  incrementOrDecrementValue: (item: IDashboardSettingEntry, direction: -1 | 1) => IDashboardSettingEntry;
54
71
  };
55
72
 
@@ -168,6 +185,7 @@ export declare interface IDynamicWidgetCatalogEntryBase<TFrameworkElementType =
168
185
  key: TDashboardWidgetKey;
169
186
  title: string;
170
187
  isContainer?: boolean;
188
+ isRemote?: boolean;
171
189
  meta?: TWidgetMetaInfoBase<TFrameworkElementType>;
172
190
  component?: TFrameworkComponentType;
173
191
  loader?: TWidgetFactoryBase<TFrameworkComponentType>;
@@ -272,6 +290,13 @@ export declare type TWidgetMetaInfoBase<TFrameworkElementType = any> = {
272
290
 
273
291
  export declare type TWidgetSize = 'default' | 'large' | 'xlarge';
274
292
 
293
+ /**
294
+ * @name useDashboardStorageService
295
+ * @description
296
+ * This implementation of IDashboardStorageService uses localStorage to store custom dashboard configurations.
297
+ * Developers can implement their own version of the dashboard storage service to store the data in a database or other way if needed.
298
+ * @returns An instance of IDashboardStorageService
299
+ */
275
300
  export declare const useDashboardStorageService: () => IDashboardStorageService;
276
301
 
277
302
  export { }
package/dist/core.es.js CHANGED
@@ -77,61 +77,68 @@ const w = [
77
77
  value: "1.0rem"
78
78
  }
79
79
  ], S = ["rem", "pc", "cm", "in", "em", "vh", "vw", "%"], b = (e, t) => S.includes(e) ? t : 1, E = {
80
+ /**
81
+ * @name incrementOrDecrementValue
82
+ * @description Increments or decrement a value based on the direction parameter
83
+ * @param item: an instance of IDashboardSettingEntry
84
+ * @param direction: -1 (for decrement) or 1 (for increment)
85
+ * @returns the update item
86
+ */
80
87
  incrementOrDecrementValue: (e, t) => {
81
- const i = e.value.match(/([\d.]+)/), a = i ? parseFloat(i[1]) : 0, s = e.value.match(/([^\d.]+)/), n = s ? s[1] : e.defaultUnit, r = b(n, e.step) * t, c = `${Math.max(a + r, e.minValue).toFixed(1)}${n}`;
88
+ const a = e.value.match(/([\d.]+)/), i = a ? parseFloat(a[1]) : 0, s = e.value.match(/([^\d.]+)/), n = s ? s[1] : e.defaultUnit, r = b(n, e.step) * t, c = `${Math.max(i + r, e.minValue).toFixed(1)}${n}`;
82
89
  return {
83
90
  ...e,
84
91
  value: c
85
92
  };
86
93
  }
87
- }, f = (e, t) => `dashboards_${t}_${e}`, v = async (e, t, i, a) => {
94
+ }, f = (e, t) => `dashboards_${t}_${e}`, v = async (e, t, a, i) => {
88
95
  const s = localStorage.getItem(f(e, t));
89
96
  if (s)
90
97
  try {
91
98
  const n = JSON.parse(s);
92
- return n.length < 1 ? [a] : (n.forEach((r) => {
93
- r.dashboardId || (r.dashboardId = "default"), r.dashboardName || (r.dashboardName = `Dashboard ${r.dashboardId}`), r.responsiveGrid = r.responsiveGrid ?? !1, (r.widgets || []).length < 1 && (r.widgets = a.widgets);
99
+ return n.length < 1 ? [i] : (n.forEach((r) => {
100
+ r.dashboardId || (r.dashboardId = "default"), r.dashboardName || (r.dashboardName = `Dashboard ${r.dashboardId}`), r.responsiveGrid = r.responsiveGrid ?? !1, (r.widgets || []).length < 1 && (r.widgets = i.widgets);
94
101
  const c = (r.cssSettings || []).filter(
95
- (o) => a.cssSettings.some((d) => d.key === o.key)
102
+ (o) => i.cssSettings.some((d) => d.key === o.key)
96
103
  );
97
104
  if (c.length < 1)
98
- r.cssSettings = [...a.cssSettings];
105
+ r.cssSettings = [...i.cssSettings];
99
106
  else {
100
107
  c.forEach((d) => {
101
108
  d.value = (d.value || "").replace(/NaN/g, "");
102
- const l = a.cssSettings.find(
109
+ const l = i.cssSettings.find(
103
110
  (g) => g.key === d.key
104
111
  );
105
112
  l && (Object.keys(l).forEach((g) => {
106
113
  g in d || (d[g] = l[g]);
107
114
  }), d.step = l.step, d.minValue = l.minValue, d.defaultValue = l.defaultValue, d.defaultUnit = l.defaultUnit, /\d+/g.test(d.value) === !1 && (d.value = l ? l.value : "1.0rem"));
108
115
  });
109
- const o = a.cssSettings.filter((d) => !c.some(
116
+ const o = i.cssSettings.filter((d) => !c.some(
110
117
  (l) => l.key === d.key
111
118
  ));
112
119
  r.cssSettings = [...c, ...o];
113
120
  }
114
121
  r.widgets = r.widgets.filter(
115
- (o) => o.includes("WidgetContainer") || i.has(o)
122
+ (o) => o.includes("WidgetContainer") || a.has(o)
116
123
  ), r.childWidgetsConfig = r.childWidgetsConfig.filter(
117
- (o) => i.has(o.widgetKey)
124
+ (o) => a.has(o.widgetKey)
118
125
  ), r.zoomScale ? r.zoomScale < 0.7 && (r.zoomScale = 0.7) : r.zoomScale = 1;
119
126
  }), n);
120
127
  } catch (n) {
121
128
  console.warn("Error parsing saved dashboard config:", n);
122
129
  }
123
- return [a];
124
- }, C = async (e, t, i, a) => {
125
- i.forEach((n) => {
130
+ return [i];
131
+ }, C = async (e, t, a, i) => {
132
+ a.forEach((n) => {
126
133
  if (n.userID = e, n.clientAppKey = t, n.responsiveGrid = n.responsiveGrid ?? !1, typeof n != "object")
127
134
  throw new Error("Invalid dashboard configuration");
128
135
  n.widgets = n.widgets.filter(
129
- (r) => r.includes("WidgetContainer") || a.has(r)
136
+ (r) => r.includes("WidgetContainer") || i.has(r)
130
137
  ), n.childWidgetsConfig = n.childWidgetsConfig.filter(
131
- (r) => a.has(r.widgetKey)
138
+ (r) => i.has(r.widgetKey)
132
139
  ), n.zoomScale ? n.zoomScale < 0.7 && (n.zoomScale = 0.7) : n.zoomScale = 1;
133
140
  });
134
- const s = JSON.stringify(i);
141
+ const s = JSON.stringify(a);
135
142
  return localStorage.setItem(f(e, t), s), !0;
136
143
  }, W = {
137
144
  getSavedDashboards: v,
@@ -153,92 +160,94 @@ const w = [
153
160
  let t = Number(e || 0);
154
161
  return t < m && (t = m), t > p && (t = p), t;
155
162
  }, j = (e, t) => {
156
- let i = Number(Number((V * t).toFixed(2)).toFixed(2)), a = Number((Number(e) + i).toFixed(2));
157
- return D(a);
163
+ let a = Number(Number((V * t).toFixed(2)).toFixed(2)), i = Number((Number(e) + a).toFixed(2));
164
+ return D(i);
158
165
  }, N = (e) => {
159
166
  let t = {
160
167
  ...e
161
168
  };
162
- return t.widgets = t.widgets.filter((i) => {
163
- if (`${i}`.includes("WidgetContainer")) {
164
- const a = t.childWidgetsConfig.filter(
165
- (s) => s.parentWidgetKey === i
169
+ return t.widgets = t.widgets.filter((a) => {
170
+ if (`${a}`.includes("WidgetContainer")) {
171
+ const i = t.childWidgetsConfig.filter(
172
+ (s) => s.parentWidgetKey === a
166
173
  );
167
- if (!a || a.length === 0)
174
+ if (!i || i.length === 0)
168
175
  return t.widgets = t.widgets.filter(
169
- (s) => s !== i
176
+ (s) => s !== a
170
177
  ), !1;
171
178
  }
172
179
  return !0;
173
180
  }), t;
174
181
  }, x = (e) => {
175
182
  const t = e.widgets.filter(
176
- (a) => a.includes("WidgetContainer")
177
- ), i = {};
178
- return t.forEach((a, s) => {
179
- const n = `${a.split("_container")[0]}_container${s + 1}`;
180
- i[a] = n;
181
- }), e.widgets = e.widgets.map((a) => i[a] || a), e.childWidgetsConfig = e.childWidgetsConfig.map((a) => {
182
- const s = a.parentWidgetKey, n = i[s];
183
+ (i) => i.includes("WidgetContainer")
184
+ ), a = {};
185
+ return t.forEach((i, s) => {
186
+ const n = `${i.split("_container")[0]}_container${s + 1}`;
187
+ a[i] = n;
188
+ }), e.widgets = e.widgets.map((i) => a[i] || i), e.childWidgetsConfig = e.childWidgetsConfig.map((i) => {
189
+ const s = i.parentWidgetKey, n = a[s];
183
190
  return {
184
- ...a,
191
+ ...i,
185
192
  // If a new key exists, use it. If not, keep the original key.
186
193
  parentWidgetKey: n || s
187
194
  };
188
195
  }), e;
189
196
  }, u = (e, t) => {
190
- const i = `${e}`.includes("Container"), a = i ? ["Container"] : ["Widget"], s = t?.name || e, n = t?.description || (i ? "Container" : "Unknown");
197
+ const a = `${e}`.includes("Container"), i = a ? ["Container"] : ["Widget"], s = t?.name || e, n = t?.description || (a ? "Container" : "Unknown");
191
198
  return {
192
199
  name: s,
193
200
  description: n,
194
- categories: a,
201
+ categories: i,
195
202
  noDuplicatedWidgets: !0,
196
203
  icon: void 0,
197
204
  externalDependencies: []
198
205
  };
199
- }, I = (e, t, i) => t[e] || u(e, i), M = (e, t) => t.get(e)?.meta || u(e), k = (e, t, i) => {
200
- const a = i || u(e);
206
+ }, I = (e, t, a) => t[e] || u(e, a), M = (e, t) => t.get(e)?.meta || u(e), k = (e, t, a) => {
207
+ const i = a || u(e);
201
208
  return [
202
209
  e,
203
210
  {
204
211
  key: e,
205
- title: a.name,
212
+ title: i.name,
206
213
  isContainer: `${e}`.includes("Container"),
207
- meta: a,
214
+ isRemote: !1,
215
+ meta: i,
208
216
  component: t
209
217
  }
210
218
  ];
211
- }, h = (e, t, i) => {
212
- const a = i || u(e);
219
+ }, h = (e, t, a, i) => {
220
+ const s = i || u(e);
213
221
  return [
214
222
  e,
215
223
  {
216
224
  key: e,
217
- title: a.name,
225
+ title: s.name,
218
226
  isContainer: !1,
219
- meta: a,
227
+ isRemote: a,
228
+ meta: s,
220
229
  loader: t
221
230
  }
222
231
  ];
223
232
  }, F = (e) => {
224
233
  const t = e.match(/\/widget-([a-zA-Z0-9-]+)\/index\.ts$/);
225
234
  if (t && t[1]) {
226
- const i = t[1], a = i.split("-"), s = `Widget${a.map((r) => r.charAt(0).toUpperCase() + r.slice(1)).join("")}`, n = a.map((r) => r.charAt(0).toUpperCase() + r.slice(1)).join(" ");
235
+ const a = t[1], i = a.split("-"), s = `Widget${i.map((r) => r.charAt(0).toUpperCase() + r.slice(1)).join("")}`, n = i.map((r) => r.charAt(0).toUpperCase() + r.slice(1)).join(" ");
227
236
  return {
228
237
  key: s,
229
238
  title: n,
230
- folder: i
239
+ folder: a
231
240
  };
232
241
  }
233
242
  return null;
234
- }, $ = (e, t, i, a) => {
235
- const s = `${t}/widget-${i}/meta.ts`, n = e[s];
243
+ }, $ = (e, t, a, i) => {
244
+ const s = `${t}/widget-${a}/meta.ts`, n = e[s];
236
245
  if (!n)
237
246
  return;
238
- const r = `${a}Meta`;
247
+ const r = `${i}Meta`;
239
248
  return n[r] || void 0;
240
- }, A = async (e) => new Promise(async (t, i) => {
241
- const a = [];
249
+ }, A = async (e) => new Promise(async (t, a) => {
250
+ const i = [];
242
251
  try {
243
252
  const s = await (await fetch(`${e}?${Math.random()}`)).json();
244
253
  for (const n in s) {
@@ -254,32 +263,32 @@ const w = [
254
263
  // Or a logic to map a string name to a Lucide component
255
264
  externalDependencies: r.meta?.externalDependencies || []
256
265
  };
257
- a.push(h(n, c, o));
266
+ i.push(h(n, c, !0, o));
258
267
  }
259
268
  t({
260
- entries: a,
269
+ entries: i,
261
270
  message: "",
262
271
  details: ""
263
272
  });
264
273
  } catch (s) {
265
- console.error("Remote plugin discovery failed:", s), i({
274
+ console.error("Remote plugin discovery failed:", s), a({
266
275
  entries: [],
267
276
  message: "Remote plugin discovery failed:",
268
277
  details: typeof s == "object" ? JSON.stringify(s) : s
269
278
  });
270
279
  }
271
- }), K = (e, t, i, a = !0) => {
280
+ }), K = (e, t, a, i = !0) => {
272
281
  const s = [];
273
282
  for (const n in t) {
274
283
  const r = t[n], c = F(n);
275
284
  if (c && r) {
276
285
  const { key: o, title: d, folder: l } = c;
277
- let g = $(i, e, l, o);
286
+ let g = $(a, e, l, o);
278
287
  if (g || (g = u(o, {
279
- description: `Local ${a ? "dynamic" : "static"} widget`
280
- })), a)
288
+ description: `Local ${i ? "dynamic" : "static"} widget`
289
+ })), i)
281
290
  s.push(
282
- h(o, r, g)
291
+ h(o, r, !1, g)
283
292
  );
284
293
  else {
285
294
  const y = r.default || r;
@@ -288,13 +297,13 @@ const w = [
288
297
  }
289
298
  }
290
299
  return s;
291
- }, G = (e, t = "color") => {
300
+ }, R = (e, t = "color") => {
292
301
  if (typeof window > "u") return "#FFFFFF";
293
- const i = document.createElement("div");
294
- Array.isArray(e) ? e.forEach((s) => i.classList.add(s)) : e.split(" ").forEach((s) => i.classList.add(s)), i.style.display = "none", document.body.appendChild(i);
295
- const a = window.getComputedStyle(i)[t];
296
- return document.body.removeChild(i), a;
297
- }, R = {
302
+ const a = document.createElement("div");
303
+ Array.isArray(e) ? e.forEach((s) => a.classList.add(s)) : e.split(" ").forEach((s) => a.classList.add(s)), a.style.display = "none", document.body.appendChild(a);
304
+ const i = window.getComputedStyle(a)[t];
305
+ return document.body.removeChild(a), i;
306
+ }, G = {
298
307
  /**
299
308
  * @name getCssVariableValue
300
309
  * @description Return the value of a CSS custom property from the current HTML document
@@ -323,8 +332,8 @@ const w = [
323
332
  */
324
333
  restoreCssVarsFromSettings: (e) => {
325
334
  const t = document.documentElement;
326
- e.forEach((i) => {
327
- t.style.setProperty(i.cssProperty, i.value);
335
+ e.forEach((a) => {
336
+ t.style.setProperty(a.cssProperty, a.value);
328
337
  });
329
338
  }
330
339
  }, Z = (e, ...t) => [
@@ -340,7 +349,7 @@ export {
340
349
  h as createDynamicEntry,
341
350
  k as createStaticEntry,
342
351
  w as cssSettingsCatalog,
343
- R as cssVarsUtils,
352
+ G as cssVarsUtils,
344
353
  E as dashboardSettingsUtils,
345
354
  x as ensureContainersSequence,
346
355
  D as ensureZoomScaleIsWithinRange,
@@ -355,6 +364,6 @@ export {
355
364
  F as parseKeyAndTitleFromFilePath,
356
365
  A as remoteWidgetDiscovery,
357
366
  N as removeEmptyContainers,
358
- G as resolveColorFromClass,
367
+ R as resolveColorFromClass,
359
368
  P as useDashboardStorageService
360
369
  };
@@ -169,6 +169,7 @@ export declare interface IDynamicWidgetCatalogEntryBase<TFrameworkElementType =
169
169
  key: TDashboardWidgetKey;
170
170
  title: string;
171
171
  isContainer?: boolean;
172
+ isRemote?: boolean;
172
173
  meta?: TWidgetMetaInfoBase<TFrameworkElementType>;
173
174
  component?: TFrameworkComponentType;
174
175
  loader?: TWidgetFactoryBase<TFrameworkComponentType>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenorlab/react-dashboard",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "Foundation components for creating user-configurable dashboards in React",
5
5
  "author": "Damiano Fusco",
6
6
  "type": "module",
@@ -54,7 +54,7 @@
54
54
  "zustand": "^5.0.0 || ^5.0.9"
55
55
  },
56
56
  "devDependencies": {
57
- "@tenorlab/dashboard-core": "^1.0.1",
57
+ "@tenorlab/dashboard-core": "^1.4.2",
58
58
  "@types/node": "^24.10.1",
59
59
  "@types/react": "19.2.3",
60
60
  "@types/react-dom": "19.2.3",