@tenorlab/vue-dashboard 1.5.45 → 1.6.1

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/LICENSE CHANGED
@@ -1,43 +1,21 @@
1
- TENORLAB DASHBOARD FRAMEWORK LICENSE
2
- Version 1.0.0
3
-
4
- Copyright (c) 2026 Tenorlab. All rights reserved.
5
-
6
- 1. DUAL-LICENSE MODEL
7
- This Software is licensed under a dual-license model. By accessing or using the
8
- source code, you agree to be bound by one of the following licenses:
9
-
10
- (a) NON-COMMERCIAL USE: If your use is Non-Commercial (as defined by the
11
- Polyform Non-Commercial License 1.0.0), your use is governed by that license.
12
- A copy can be found at: https://polyformproject.org/licenses/non-commercial/1.0.0/
13
-
14
- (b) COMMERCIAL USE: If your use is Commercial, you must obtain a valid
15
- Tenorlab Commercial License. Commercial use includes, but is not limited to:
16
- building for a client, use within a for-profit entity (internal or external),
17
- or use in any revenue-generating product. Licenses are available at:
18
- https://www.tenorlab.com
19
-
20
- 2. REDISTRIBUTION RESTRICTIONS
21
- Notwithstanding the terms of any sub-license mentioned above, the
22
- redistribution of the Software's source code—whether in its original form
23
- or as a modified derivative work—is STRICTLY PROHIBITED.
24
-
25
- You may not include this Software, or any portion thereof, in any:
26
- - Software Development Kit (SDK)
27
- - UI Component Library or Framework
28
- - Website/Dashboard Starter Template or Boilerplate
29
- - "Dashboard Builder" or similar tool intended for use by other developers.
30
-
31
- 3. DERIVATIVE WORKS & MODIFICATIONS
32
- You are permitted to modify and extend the Software for the sole purpose of
33
- building a specific "End Product" application. Modifying the code does not
34
- transfer ownership of the underlying logic, nor does it grant permission
35
- to bypass the Commercial License requirement or the Redistribution Restrictions.
36
-
37
- 4. TERMINATION
38
- Failure to comply with these terms automatically terminates your right to
39
- use the Software. Tenorlab reserves all rights not expressly granted herein.
40
-
41
- 5. CONTACT & INQUIRIES
42
- For licensing questions or professional inquiries, please visit:
43
- https://www.tenorlab.com
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tenorlab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,38 +1,37 @@
1
1
  # @tenorlab/vue-dashboard
2
2
 
3
- [![License: Polyform-NC-1.0.0](https://img.shields.io/badge/License-Polyform--NC--1.0.0-blue.svg)](https://polyformproject.org/licenses/non-commercial/1.0.0/)
4
- [![License: Tenorlab-Commercial](https://img.shields.io/badge/License-Tenorlab--Commercial-gold.svg)](https://payhip.com/b/gPBpo)
5
- [![Redistribution: Prohibited](https://img.shields.io/badge/Redistribution-Prohibited-red.svg)](#-distribution-restrictions)
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
4
+ [![Template: Pro](https://img.shields.io/badge/Template-Pro--Available-gold.svg)](https://www.tenorlab.com)
5
+ [![Framework: Vue](https://img.shields.io/badge/Framework-Vue-42b883.svg)](https://vuejs.org/)
6
6
 
7
- Foundation components for creating user-configurable, high-performance dashboards in Vue. Built on top of **@tenorlab/dashboard-core**.
7
+ Foundation components for creating user-configurable, high-performance dashboards in Vue.
8
8
 
9
- ## 🏗 Relationship to Core
9
+ ## Relationship to Core
10
10
 
11
11
  This package extends **@tenorlab/dashboard-core**. It provides the Vue implementation of the core logic, including specialized hooks, state management, and a suite of UI components.
12
12
 
13
13
  > **Note**: This package re-exports all types and utilities from `@tenorlab/dashboard-core`. You do not need to install the core package separately.
14
14
 
15
15
 
16
- ## Demos
17
- -- [React Demo](https://react.tenorlab.com) (built with @tenorlab/react-dashboard)
18
- -- [Vue Demo](https://vue.tenorlab.com) (built with @tenorlab/vue-dashboard)
16
+ ## Pro Template Demos
17
+ - [React Demo](https://react.tenorlab.com) (built with @tenorlab/react-dashboard)
18
+ - [Vue Demo](https://vue.tenorlab.com) (built with @tenorlab/vue-dashboard)
19
+ - [Nuxt Demo](https://nuxt.tenorlab.com) (built with @tenorlab/vue-dashboard)
19
20
 
20
21
 
21
22
  ## ✨ Features
22
23
 
23
- - **Type-Safe:** Deep integration with TypeScript 5.8+ for full IDE support.
24
- - **State Management:** Built-in `useDashboardStore` and `useDashboardUndoService`.
25
- - **User Configurable:** Ready-to-use components for adding, removing, and dragging widgets.
26
- - **Themeable:** Native support for CSS Variables and Tailwind CSS.
27
- - **Vite Optimized:** Full ESM support and tree-shakeable.
24
+ - **Type-Safe:** Deep integration with TypeScript 5.8+ for full IDE support.
25
+ - **State Management:** Built-in `useDashboardStore` and `useDashboardUndoService`.
26
+ - **User Configurable:** Ready-to-use components for adding, removing, and dragging widgets.
27
+ - **Themeable:** Native support for CSS Variables and Tailwind CSS.
28
+ - **Vite Optimized:** Full ESM support and tree-shakeable.
28
29
 
29
30
  ## 🚀 Quick Start
30
31
 
31
32
  ### Installation
32
33
 
33
- Bash
34
-
35
- ```
34
+ ```bash
36
35
  # with npm
37
36
  npm i @tenorlab/vue-dashboard
38
37
 
@@ -56,42 +55,35 @@ import '@tenorlab/vue-dashboard/styles.css'
56
55
 
57
56
  Widgets should be organized by their loading strategy.
58
57
 
59
- - **Bundled Widgets**: Place in `src/bundled-widgets/` (loaded immediately).
60
- - **Async Widgets**: Place in `src/async-widgets/` (lazy-loaded).
58
+ - **Bundled Widgets**: Place in `src/bundled-widgets/` (loaded immediately).
59
+ - **Async Widgets**: Place in `src/async-widgets/` (lazy-loaded).
60
+
61
+ *(NOTE: These directory names are suggestions; you can use different names, or put the widgets under src/components if you prefer)*
62
+
61
63
 
62
64
  Each widget requires a sub-directory using the `widget-name-here` convention.
63
65
 
64
66
  #### Example: `WidgetTotalOrders`
65
67
 
66
68
  Directory name `widget-total-orders`, files:
67
- - WidgetTotalOrders.vue
68
- - meta.ts
69
- - index.ts
69
+ - WidgetTotalOrders.vue
70
+ - meta.ts
71
+ - index.ts
70
72
 
71
- ```Vue
72
- // file: src/bundled-widgets/widget-total-orders/WidgetTotalOrders.vue
73
+ File: src/bundled-widgets/widget-total-orders/WidgetTotalOrders.vue:
74
+ ```vue
73
75
  <script setup lang="ts">
74
- import type {
75
- IDashboardWidgetProps,
76
- TDashboardWidgetKey,
77
- TWidgetEmits,
78
- } from '@tenorlab/vue-dashboard'
76
+ import type { IDashboardWidgetProps, TWidgetEmits } from '@tenorlab/vue-dashboard'
79
77
  import { DashboardWidgetBase, WrapperColumnContent, useWidgetEmits } from '@tenorlab/vue-dashboard'
80
78
 
81
- defineProps<IDashboardWidgetProps>()
79
+ const props = defineProps<IDashboardWidgetProps>()
82
80
  const emits = defineEmits<TWidgetEmits>()
83
- const { removeClick: onRemoveClick, moveClick: onMoveClick } = useWidgetEmits(emits)
81
+ const emitHandlers = useWidgetEmits(emits)
84
82
  </script>
85
83
 
86
84
  <template>
87
- <DashboardWidgetBase
88
- v-bind="props"
89
- widgetKey="WidgetTotalOrders"
90
- title="Total Orders"
91
- @removeClick="onRemoveClick"
92
- @moveClick="onMoveClick"
93
- >
94
- <WrapperColumnContent>
85
+ <DashboardWidgetBase v-bind="props" v-on="emitHandlers">
86
+ <WrapperColumnContent><!-- WrapperColumnContent is optional, you are free to use a div or other html elements here -->
95
87
  <div class="dashboard-number number-xl text-primary">1,250</div>
96
88
  <div class="text-sm">Orders this month</div>
97
89
  </WrapperColumnContent>
@@ -99,11 +91,11 @@ const { removeClick: onRemoveClick, moveClick: onMoveClick } = useWidgetEmits(em
99
91
  </template>
100
92
  ```
101
93
 
94
+ File: src/bundled-widgets/widget-total-orders/meta.ts:
102
95
  ```typescript
103
- // file: src/bundled-widgets/widget-total-orders/meta.ts
104
96
  import type { TWidgetMetaInfo } from '@tenorlab/vue-dashboard'
105
- import { MonitorIcon as ComponentIcon } from '@tenorlab/vue-dashboard'
106
97
  import { markRaw } from 'vue'
98
+ import { ReceiptIcon as ComponentIcon } from 'lucide-vue-next'
107
99
 
108
100
  // Define the metadata object for the plugin
109
101
  export const WidgetTotalOrdersMeta: TWidgetMetaInfo = {
@@ -116,8 +108,8 @@ export const WidgetTotalOrdersMeta: TWidgetMetaInfo = {
116
108
  }
117
109
  ```
118
110
 
111
+ File: src/bundled-widgets/widget-total-orders/index.ts:
119
112
  ```typescript
120
- // file: src/bundled-widgets/widget-total-orders/index.ts
121
113
  import WidgetTotalOrders from './WidgetTotalOrders.vue'
122
114
  export default WidgetTotalOrders
123
115
  ```
@@ -128,25 +120,26 @@ export default WidgetTotalOrders
128
120
  Create `src/widgets-catalog.ts` in your project root. This file manages how widgets are discovered (locally via Vite's `import.meta.glob` or remotely via CDN).
129
121
 
130
122
 
123
+ File: src/widgets-catalog.ts:
131
124
  ```typescript
132
- // file: src/widgets-catalog.ts
125
+ import { WidgetContainerColumn, WidgetContainerLarge, WidgetContainerRow } from '@tenorlab/vue-dashboard'
126
+ import { createStaticEntry, localWidgetDiscovery, remoteWidgetDiscovery } from '@tenorlab/vue-dashboard/core'
127
+ import { getWidgetsManifestUrl } from '@/utils/'
128
+
129
+ // optional: other static widgets to add manually
133
130
  import {
131
+ WidgetSmallCardSample,
132
+ } from './other-widgets/other-widgets'
133
+ // meta data map for other static widgets:
134
+ import { otherWidgetsMetaMap } from './other-widgets/other-widgets-meta'
135
+ import type {
134
136
  IDynamicWidgetCatalogEntry,
135
137
  TDashboardWidgetCatalog,
136
138
  TWidgetMetaInfoBase,
137
- WidgetContainerColumn,
138
- WidgetContainerLarge,
139
- WidgetContainerRow,
140
139
  TWidgetFactory,
140
+ TDashboardWidgetKey,
141
+ TWidgetMetaInfo,
141
142
  } from '@tenorlab/vue-dashboard'
142
- import {
143
- createStaticEntry,
144
- localWidgetDiscovery,
145
- remoteWidgetDiscovery,
146
- } from '@tenorlab/vue-dashboard/core'
147
-
148
- import WidgetRecentPaymentInfo from './other-widgets/WidgetRecentPaymentInfo.vue'
149
- //import { getWidgetsManifestUrl } from '@/utils'
150
143
 
151
144
  const bundledWidgetsSrcPath = '/src/bundled-widgets'
152
145
  const asyncWidgetsSrcPath = '/src/async-widgets'
@@ -169,7 +162,7 @@ const allMetaModules = import.meta.glob('/src/**/widget-*/meta.ts', {
169
162
  eager: true,
170
163
  }) as Record<string, Record<string, TWidgetMetaInfoBase>>
171
164
 
172
- const hasPermission = (_user: any, _permission: string) => true
165
+ const hasPermission = (_user_: any, _permission: string) => true
173
166
 
174
167
  /**
175
168
  * @name getWidgetCatalog
@@ -178,34 +171,51 @@ const hasPermission = (_user: any, _permission: string) => true
178
171
  export const getWidgetCatalog = async (user: any | null): Promise<TDashboardWidgetCatalog> => {
179
172
  // A. Register Static Core Components
180
173
  const catalogMapEntries: [string, IDynamicWidgetCatalogEntry][] = [
181
- createStaticEntry('WidgetContainer', WidgetContainerColumn),
182
- createStaticEntry('WidgetContainerRow', WidgetContainerRow),
183
- createStaticEntry('WidgetContainerLarge', WidgetContainerLarge),
174
+ createStaticEntry('WidgetContainer', WidgetContainerColumn, otherWidgetsMetaMap['WidgetContainer']),
175
+ createStaticEntry('WidgetContainerRow', WidgetContainerRow, otherWidgetsMetaMap['WidgetContainerRow']),
176
+ createStaticEntry(
177
+ 'WidgetContainerLarge',
178
+ WidgetContainerLarge,
179
+ otherWidgetsMetaMap['WidgetContainerLarge'],
180
+ ),
184
181
  ]
185
182
 
186
- // B. Register Business Static Widgets
187
- // we could filter further by permissions and user type if needed
183
+ // B. Optional: Register Business Static Widgets manually:
184
+ // Example: you could filter further by permissions and user type if needed
188
185
  if (hasPermission(user, 'some-permission')) {
189
- catalogMapEntries.push(createStaticEntry('WidgetRecentPaymentInfo', WidgetRecentPaymentInfo))
186
+ // i.e.:
187
+ // catalogMapEntries.push(
188
+ // createStaticEntry(
189
+ // 'WidgetThatRequiresPermissions',
190
+ // WidgetThatRequiresPermissions,
191
+ // otherWidgetsMetaMap['WidgetThatRequiresPermissions'],
192
+ // ),
193
+ // )
190
194
  }
191
195
 
192
- // add bundled-widgets
193
- catalogMapEntries.push(...localWidgetDiscovery(
194
- bundledWidgetsSrcPath,
195
- bundledWidgetModules,
196
- allMetaModules,
197
- false, // lazy: false
198
- ))
199
-
200
- // Async widgets (dynamically loaded, like plugins)
201
- catalogMapEntries.push(...localWidgetDiscovery(
202
- asyncWidgetsSrcPath,
203
- asyncWidgetModules,
204
- allMetaModules,
205
- true, // lazy: true
206
- ))
207
-
208
- // Optional: Remote discovery of -pre-built widgets hosted on a CDN
196
+ // C. Register widgets automatically with the localWidgetDiscovery helper:
197
+ // (bundled widgets are included always, non-lazy)
198
+ catalogMapEntries.push(
199
+ ...localWidgetDiscovery(
200
+ bundledWidgetsSrcPath,
201
+ bundledWidgetModules,
202
+ allMetaModules,
203
+ false, // lazy: false
204
+ ),
205
+ )
206
+
207
+ // D. Register "lazy" widgets automatically with the localWidgetDiscovery helper:
208
+ // (async widgets are not incuded, they are lazy loaded at run time)
209
+ catalogMapEntries.push(
210
+ ...localWidgetDiscovery(
211
+ asyncWidgetsSrcPath,
212
+ asyncWidgetModules,
213
+ allMetaModules,
214
+ true, // lazy: true
215
+ )
216
+ )
217
+
218
+ // E. Optional: Remote discovery of -pre-built widgets hosted on a CDN (requires advance importMaps setup and other configuration)
209
219
  /*const manifestUrl = getWidgetsManifestUrl()
210
220
  if (manifestUrl.length > 0) {
211
221
  const remoteResponse = await remoteWidgetDiscovery(manifestUrl)
@@ -222,22 +232,23 @@ export const getWidgetCatalog = async (user: any | null): Promise<TDashboardWidg
222
232
 
223
233
  Use a `dashboard-defaults.ts` file to define initial layouts based on user roles.
224
234
 
235
+ File: src/dashboard-defaults.ts:
225
236
  ```typescript
226
- // file: src/dashboard-defaults.ts
227
- import {
237
+ import { blankDashboardConfig, cssSettingsCatalog } from '@tenorlab/vue-dashboard/core'
238
+ import { getWidgetCatalog } from './widgets-catalog'
239
+ import type {
228
240
  TDashboardWidgetKey,
229
241
  IChildWidgetConfigEntry,
230
242
  IDashboardConfig,
231
243
  TDashboardWidgetCatalog,
232
244
  } from '@tenorlab/vue-dashboard'
233
- import { blankDashboardConfig, cssSettingsCatalog } from '@tenorlab/vue-dashboard/core'
234
- import { getWidgetCatalog } from './widgets-catalog'
235
245
 
236
246
  // reserved identifier to be used only for the default dashboard
237
247
  const DEFAULT_DASHBOARD_ID = 'default' as const
238
248
  const DEFAULT_DASHBOARD_NAME = 'Default' as const
239
249
 
240
- const getDefaultDashboardForCustomerUser = (
250
+ // default dashboard config for Regular user type
251
+ const getDefaultDashboardForRegularUser = (
241
252
  user: any,
242
253
  clientAppKey: string,
243
254
  availableWidgetKeys: TDashboardWidgetKey[]
@@ -249,10 +260,21 @@ const getDefaultDashboardForCustomerUser = (
249
260
  dashboardId: DEFAULT_DASHBOARD_ID,
250
261
  dashboardName: DEFAULT_DASHBOARD_NAME,
251
262
  zoomScale: 1,
252
- responsiveGrid: false,
253
- widgets: ['WidgetContainer_container1'],
263
+ responsiveGrid: true,
264
+ widgets: [
265
+ 'WidgetContainer_container1', // will contain other widgets specified in the childWidgetsConfig secitno below
266
+ 'WidgetBarGradients',
267
+ ],
254
268
  childWidgetsConfig: [
255
- { parentWidgetKey: 'WidgetContainer_container1', widgetKey: 'WidgetRecentPaymentInfo' }
269
+ // two widgets go into container1:
270
+ {
271
+ parentWidgetKey: 'WidgetContainer_container1',
272
+ widgetKey: 'WidgetTotalOrders'
273
+ },
274
+ {
275
+ parentWidgetKey: 'WidgetContainer_container1',
276
+ widgetKey: 'WidgetTotalOrders'
277
+ }
256
278
  ],
257
279
  cssSettings: [...cssSettingsCatalog]
258
280
  }
@@ -267,10 +289,16 @@ export const getDashboardDefaults = async (
267
289
  }> => {
268
290
  const widgetsCatalog = await getWidgetCatalog(user)
269
291
 
270
- if (!user) return { dashboardConfig: blankDashboardConfig, widgetsCatalog }
292
+ if (!user) {
293
+ return {
294
+ dashboardConfig: blankDashboardConfig,
295
+ widgetsCatalog,
296
+ }
297
+ }
271
298
 
272
299
  return {
273
- dashboardConfig: getDefaultDashboardForCustomerUser(user, clientAppKey, [...widgetsCatalog.keys()]),
300
+ // Optional, you could use different routines depending on user role:
301
+ dashboardConfig: getDefaultDashboardForRegularUser(user, clientAppKey, [...widgetsCatalog.keys()]),
274
302
  widgetsCatalog
275
303
  }
276
304
  }
@@ -280,15 +308,11 @@ export const getDashboardDefaults = async (
280
308
 
281
309
  Use this for a simplified, non-editable view of the dashboard.
282
310
 
283
- TypeScript
284
-
311
+ File: src/views/DashboardReadonly.vue:
285
312
  ```vue
286
313
  <script setup lang="ts">
287
- // file: src/views/DashboardReadonly.vue
288
- import { reactive, watch, onMounted } from 'vue'
314
+ import { reactive, shallowRef, watch, onMounted } from 'vue'
289
315
  import {
290
- IDashboardConfig,
291
- TDashboardWidgetCatalog,
292
316
  useDashboardStore,
293
317
  } from '@tenorlab/vue-dashboard'
294
318
  import {
@@ -298,6 +322,10 @@ import {
298
322
  } from '@tenorlab/vue-dashboard/core'
299
323
  import { DynamicWidgetLoader, DashboardGrid } from '@tenorlab/vue-dashboard'
300
324
  import { getDashboardDefaults } from '../dashboard-defaults'
325
+ import type {
326
+ IDashboardConfig,
327
+ TDashboardWidgetCatalog,
328
+ } from '@tenorlab/vue-dashboard'
301
329
 
302
330
  const clientAppKey = 'myclientapp'
303
331
  const user = { id: 1234 }
@@ -312,15 +340,13 @@ const {
312
340
  targetContainerKey,
313
341
  } = dashboardStore.computed
314
342
 
315
- type TState = {
316
- defaultDashboardConfig: IDashboardConfig
317
- widgetsCatalog: TDashboardWidgetCatalog
318
- }
319
-
343
+ // 1. Standard reactive state for primitive data
320
344
  const localState = reactive<TState>({
321
345
  defaultDashboardConfig: blankDashboardConfig,
322
- widgetsCatalog: new Map(),
323
346
  })
347
+ // 2. Shallow ref for the Catalog (Zero deep-proxying)
348
+ const widgetsCatalog = shallowRef<TDashboardWidgetCatalog>(new Map())
349
+ const getWidgetCatalog = () => widgetsCatalog.value
324
350
 
325
351
  const getDefaultDashboardConfig = (): IDashboardConfig => {
326
352
  return localState.defaultDashboardConfig
@@ -331,11 +357,11 @@ async function _fetchDashboardConfig() {
331
357
  const savedConfigs = await dashboardStorageService.getSavedDashboards(
332
358
  userId,
333
359
  clientAppKey,
334
- localState.widgetsCatalog,
360
+ getWidgetCatalog(),
335
361
  defaultConfig,
336
362
  )
337
363
  dashboardStore.setAllDashboardConfigs(savedConfigs)
338
- // show default or first dashboard
364
+ // show default dashboard or first dashboard
339
365
  const dashboardConfig =
340
366
  savedConfigs.find((x) => x.dashboardId === 'default') || savedConfigs[0] || defaultConfig
341
367
  dashboardStore.setCurrentDashboardConfig(dashboardConfig)
@@ -346,7 +372,7 @@ async function _fetchDashboardConfig() {
346
372
  onMounted(async () => {
347
373
  const defaults = await getDashboardDefaults(user, clientAppKey)
348
374
  localState.defaultDashboardConfig = defaults.dashboardConfig
349
- localState.widgetsCatalog = defaults.widgetsCatalog
375
+ widgetsCatalog.value = defaults.widgetsCatalog
350
376
  await _fetchDashboardConfig()
351
377
  })
352
378
  </script>
@@ -367,7 +393,7 @@ onMounted(async () => {
367
393
  :index="index"
368
394
  :maxIndex="currentDashboardConfig.widgets.length - 1"
369
395
  :childWidgetsConfig="currentDashboardConfig.childWidgetsConfig"
370
- :widgetCatalog="localState.widgetsCatalog"
396
+ :widgetCatalog="widgetsCatalog"
371
397
  :isEditing="isEditing"
372
398
  :extraProps="dashboardContext"
373
399
  @removeClick="() => {}"
@@ -382,8 +408,7 @@ onMounted(async () => {
382
408
 
383
409
  #### 5. Full Editable Dashboard
384
410
 
385
- For a complete example including **Undo/Redo**, **Zooming**, **Catalog Flyouts**, and **Multiple Dashboards**, please refer to the [Full Implementation Example](https://github.com/tenorlab/vue-dashboard-sample/blob/main/views/DashboardFullExample.vue).
386
-
411
+ For editable dashboard examples, including **Undo/Redo**, **Zooming**, **Catalog Flyouts**, and **Multiple Dashboards**, please refer to the [Pro Template](https://www.tenorlab.com).
387
412
 
388
413
 
389
414
  ------
@@ -392,28 +417,35 @@ For a complete example including **Undo/Redo**, **Zooming**, **Catalog Flyouts**
392
417
 
393
418
  ### UI Components
394
419
 
395
- - **`DashboardGrid`**: The main layout engine for positioning widgets.
396
- - **`WidgetContainer`**: Wrapper providing common widget UI (headers, actions, loading states).
397
- - **`WidgetsCatalogFlyout`**: A slide-out panel for users to browse and add new widgets.
398
- - **`DynamicWidgetLoader`**: Lazy-loading utility for high-performance dashboards.
420
+ - **`DashboardGrid`**: The main dashboard layout that position widgets within a responsive grid.
421
+ - **`WidgetContainer`**: A special "widget" that is a container for other widgets.
422
+ - **`WidgetsCatalogFlyout`**: A slide-out panel for users to browse and add new widgets on editable dashboards.
423
+ - **`DynamicWidgetLoader`**: The core lazy-loading widget loader that renders the widgets within the grid.
399
424
 
400
425
  ### Hooks & State
401
426
 
402
427
  - **`useDashboardStore`**: Access the underlying reactive store to manage widget state, layout, and configuration.
403
- - **`useDashboardUndoService`**: Provides `undo` and `redo` functionality for user layout changes.
428
+ - **`useDashboardUndoService`**: Provides `undo` and `redo` functionality for user layout changes in editable dashboard (optional).
404
429
 
405
430
 
406
431
  ------
407
432
 
408
433
 
409
434
  ## Links
435
+
436
+ ### Open source core packages
410
437
  - [@tenorlab/react-dashboard](https://www.npmjs.com/package/@tenorlab/react-dashboard): React-specific components
411
438
  - [@tenorlab/vue-dashboard](https://www.npmjs.com/package/@tenorlab/vue-dashboard): Vue-specific components
412
- - [Official Website](https://www.tenorlab.com)
439
+
440
+ ### Pro Template Demos
413
441
  - [React Demo](https://react.tenorlab.com) (built with @tenorlab/react-dashboard)
414
442
  - [Vue Demo](https://vue.tenorlab.com) (built with @tenorlab/vue-dashboard)
443
+ - [Nuxt Demo](https://nuxt.tenorlab.com) (built with @tenorlab/vue-dashboard)
444
+
445
+ ### Others
415
446
  - [Buy a License](https://payhip.com/b/gPBpo)
416
447
  - [Follow on BlueSky](https://bsky.app/profile/tenorlab.bsky.social)
448
+ - [Official Website](https://www.tenorlab.com)
417
449
 
418
450
 
419
451
  ------
@@ -421,21 +453,15 @@ For a complete example including **Undo/Redo**, **Zooming**, **Catalog Flyouts**
421
453
 
422
454
  ## ⚖️ Licensing & Usage
423
455
 
424
- This project is **Source-Available**. You are free to view, learn from, and experiment with the source code. However, use in professional or revenue-generating settings requires a license.
456
+ **@tenorlab/vue-dashboard** is [MIT licensed](https://opensource.org/licenses/MIT).
425
457
 
426
- ### 🏠 Non-Commercial Use (Free)
427
- If you are a student, hobbyist, or working on a non-profit open-source project, you may use this library for free under the terms of the **Polyform Non-Commercial 1.0.0** license.
458
+ It provides the foundational components and logic for building dashboards. You are free to use it in any project, personal or commercial.
428
459
 
429
- ### 💼 Commercial Use (Requires License)
430
- A **Tenorlab Commercial License** is required if you are:
431
- * Building a dashboard for company internal or external use, including all company websites and applications.
432
- * Building a dashboard for a paying client.
433
- * Using the library within a for-profit company or startup.
434
- * Including the library in a product that is sold or requires a subscription.
460
+ ## ⚡️ Go Pro and Save Time: Tenorlab App Template
435
461
 
436
- **Purchase a Commercial License here:** [https://www.tenorlab.com](https://www.tenorlab.com)
462
+ A commercial license for a full-blown professional app template is available for purchase [**here**](https://www.tenorlab.com) and comes with:
437
463
 
438
- ### 🚫 Distribution Restrictions
439
- To protect the integrity of this framework, **redistribution of the source code is strictly prohibited**.
440
- * You **may** use and modify the code to build an "End Product" (e.g., a dashboard for a specific app).
441
- * You **may NOT** redistribute the source code (original or modified) as a standalone library, UI kit, starter template, or "dashboard builder" for other developers to use.
464
+ * **Full Application Shell:** A clean, optimized Vite + TypeScript project structure (with either React, Vue or Nuxt).
465
+ * **Dashboard Management:** Production-ready logic for creating, listing, renaming, and deleting multiple user-defined dashboards.
466
+ * **Implementation Examples:** Best patterns for both "Read-Only" (Analyst view) and "User-Editable" (Admin view) dashboard modes, a dynamic dashboard menu, etc.
467
+ * **Tenorlab Theme Engine:** A sophisticated Tailwind-based system supporting multiple custom themes (not just Light/Dark mode).
@@ -1,4 +1,4 @@
1
- import { reactive as de, computed as h, readonly as we, defineComponent as y, createElementBlock as g, openBlock as i, normalizeClass as p, createElementVNode as o, createStaticVNode as G, withModifiers as H, normalizeStyle as ne, renderSlot as R, mergeProps as Z, createBlock as E, withCtx as B, unref as m, toDisplayString as M, ref as j, onMounted as ye, onUnmounted as be, createVNode as I, Fragment as U, renderList as P, shallowRef as re, watchEffect as _e, markRaw as J, defineAsyncComponent as ke, resolveComponent as $e, Suspense as Ie, resolveDynamicComponent as ce, createSlots as De, createTextVNode as ue, withKeys as ae, watch as Be, createCommentVNode as ee } from "vue";
1
+ import { reactive as de, computed as h, readonly as we, defineComponent as y, openBlock as i, createElementBlock as g, normalizeClass as p, createElementVNode as o, createStaticVNode as G, withModifiers as H, normalizeStyle as ne, renderSlot as R, mergeProps as Z, createBlock as E, withCtx as B, unref as m, toDisplayString as M, ref as j, onMounted as ye, onUnmounted as be, createVNode as I, Fragment as U, renderList as P, shallowRef as re, watchEffect as _e, markRaw as J, defineAsyncComponent as ke, resolveComponent as $e, Suspense as Ie, resolveDynamicComponent as ce, createSlots as De, createTextVNode as ue, withKeys as ae, watch as Be, createCommentVNode as ee } from "vue";
2
2
  import { blankDashboardConfig as oe, dashboardStoreUtils as q, getDistinctCssClasses as N, ensureZoomScaleIsWithinRange as Ke, parseContainerTitle as ge, dashboardSettingsUtils as We, getWidgetMetaFromCatalog as Me } from "./core.es.js";
3
3
  const Ee = () => ({
4
4
  isLoading: !1,
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@tenorlab/vue-dashboard",
3
- "version": "1.5.45",
3
+ "version": "1.6.1",
4
4
  "description": "Foundation components for creating user-configurable dashboards in Vue",
5
5
  "author": "Damiano Fusco",
6
- "license": "SEE LICENSE IN LICENSE",
6
+ "license": "MIT",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -50,7 +50,7 @@
50
50
  "main": "./dist/vue-dashboard.es.js",
51
51
  "module": "./dist/vue-dashboard.es.js",
52
52
  "devDependencies": {
53
- "@tenorlab/dashboard-core": "^1.5.5",
53
+ "@tenorlab/dashboard-core": "^1.6.0",
54
54
  "@types/node": "^24.10.1",
55
55
  "@vitejs/plugin-vue": "^6.0.3",
56
56
  "prettier": "^3.7.4",