@nova-design-system/nova-vue 3.20.0 → 3.21.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -20
- package/dist/components/NvDatatable.d.ts +44 -0
- package/dist/components/NvDatatable.js +215 -137
- package/dist/generated/components.d.ts +11 -0
- package/dist/generated/components.js +46 -2
- package/dist/providers/NotificationService.d.ts +2 -0
- package/dist/providers/NotificationService.js +27 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,26 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
**Nova Components Vue** provides an easy way to use [Nova’s native Web Components](https://www.npmjs.com/package/@nova-design-system/nova-webcomponents) within your Vue applications.
|
|
4
4
|
|
|
5
|
-
- [Nova Components Vue](#nova-components-vue)
|
|
6
|
-
- [Key Features](#key-features)
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Setting up Tailwind](#setting-up-tailwind)
|
|
9
|
-
- [About Tailwind and the Nova Plugin](#about-tailwind-and-the-nova-plugin)
|
|
10
|
-
- [1. Install Tailwind CSS and the Vite Plugin](#1-install-tailwind-css-and-the-vite-plugin)
|
|
11
|
-
- [2. Configure the Vite Plugin](#2-configure-the-vite-plugin)
|
|
12
|
-
- [3. Create `tailwind.config.ts`](#3-create-tailwindconfigts)
|
|
13
|
-
- [4. Configure Tailwind and Nova Plugin in `main.css`](#4-configure-tailwind-and-nova-plugin-in-maincss)
|
|
14
|
-
- [5. Register NovaComponents and include the Nova Tokens](#5-register-novacomponents-and-include-the-nova-tokens)
|
|
15
|
-
- [6. Use Nova Components with Tailwind Utilities](#6-use-nova-components-with-tailwind-utilities)
|
|
16
|
-
- [7. Setup the Nova Font](#7-setup-the-nova-font)
|
|
17
|
-
- [Creating Your Own Style Components with Tailwind](#creating-your-own-style-components-with-tailwind)
|
|
18
|
-
- [Nova Font Pro Integration](#nova-font-pro-integration)
|
|
19
|
-
- [Option 1: Import in Global CSS (Recommended)](#option-1-import-in-global-css-recommended)
|
|
20
|
-
- [Option 2: HTML Integration](#option-2-html-integration)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
5
|
## Key Features
|
|
26
6
|
|
|
27
7
|
- **Lightweight Integration**: Leverage Nova Web Components with minimal configuration in Vue.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type VNode, type PropType } from 'vue';
|
|
2
|
+
import { type SortingState } from '@tanstack/vue-table';
|
|
2
3
|
/**
|
|
3
4
|
* Creates a typed NvDatatable component for a specific row type. This is the
|
|
4
5
|
* standard approach for generic components in Vue.
|
|
@@ -21,6 +22,10 @@ export declare function createNvDatatable<T>(): import("vue").DefineComponent<im
|
|
|
21
22
|
type: PropType<NvDatatablePaginationConfig>;
|
|
22
23
|
default: any;
|
|
23
24
|
};
|
|
25
|
+
sorting: {
|
|
26
|
+
type: PropType<NvDatatableSortingConfig>;
|
|
27
|
+
default: any;
|
|
28
|
+
};
|
|
24
29
|
renderPagination: {
|
|
25
30
|
type: PropType<(api: NvDatatableRenderPaginationAPI) => VNode>;
|
|
26
31
|
default: any;
|
|
@@ -46,6 +51,10 @@ export declare function createNvDatatable<T>(): import("vue").DefineComponent<im
|
|
|
46
51
|
type: PropType<NvDatatablePaginationConfig>;
|
|
47
52
|
default: any;
|
|
48
53
|
};
|
|
54
|
+
sorting: {
|
|
55
|
+
type: PropType<NvDatatableSortingConfig>;
|
|
56
|
+
default: any;
|
|
57
|
+
};
|
|
49
58
|
renderPagination: {
|
|
50
59
|
type: PropType<(api: NvDatatableRenderPaginationAPI) => VNode>;
|
|
51
60
|
default: any;
|
|
@@ -58,6 +67,7 @@ export declare function createNvDatatable<T>(): import("vue").DefineComponent<im
|
|
|
58
67
|
columns: NvDatatableColumn<T, keyof T, T[keyof T]>[];
|
|
59
68
|
rows: T[];
|
|
60
69
|
pagination: NvDatatablePaginationConfig;
|
|
70
|
+
sorting: NvDatatableSortingConfig;
|
|
61
71
|
renderPagination: (api: NvDatatableRenderPaginationAPI) => VNode;
|
|
62
72
|
stickyHeader: boolean;
|
|
63
73
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -152,6 +162,16 @@ export interface NvDatatableColumn<Row, K extends keyof Row = keyof Row, F = Row
|
|
|
152
162
|
valueFormatter?: (params: NvTableValueFormatterParams<Row, Row[K], K>) => F;
|
|
153
163
|
/** Custom cell renderer */
|
|
154
164
|
renderCell?: (params: NvTableRenderCellParams<Row, F, K>) => VNode | string | number;
|
|
165
|
+
/** Enable/disable sorting for this column */
|
|
166
|
+
sortable?: boolean;
|
|
167
|
+
/** Custom sorting function or built-in function name */
|
|
168
|
+
sortingFn?: ((rowA: any, rowB: any, columnId: string) => number) | string;
|
|
169
|
+
/** Start with descending sort for this column */
|
|
170
|
+
sortDescFirst?: boolean;
|
|
171
|
+
/** Invert the sort order (useful for rankings) */
|
|
172
|
+
invertSorting?: boolean;
|
|
173
|
+
/** Where to place undefined values in sort */
|
|
174
|
+
sortUndefined?: 'first' | 'last' | false | -1 | 1;
|
|
155
175
|
}
|
|
156
176
|
/**
|
|
157
177
|
* Parameters for custom cell rendering function.
|
|
@@ -241,3 +261,27 @@ export interface NvDatatableRenderPaginationAPI {
|
|
|
241
261
|
/** Whether more items are available (only for infinite scroll) */
|
|
242
262
|
hasMore?: boolean;
|
|
243
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Sorting configuration for NvDatatable.
|
|
266
|
+
* Supports both client-side and server-side sorting.
|
|
267
|
+
*/
|
|
268
|
+
export interface NvDatatableSortingConfig {
|
|
269
|
+
/** Sorting mode */
|
|
270
|
+
mode: 'client' | 'server';
|
|
271
|
+
/** Enable multi-column sorting with Shift+Click */
|
|
272
|
+
enableMultiSort?: boolean;
|
|
273
|
+
/** Allow cycling through to "no sort" state */
|
|
274
|
+
enableSortingRemoval?: boolean;
|
|
275
|
+
/** Maximum number of columns for multi-sort */
|
|
276
|
+
maxMultiSortColCount?: number;
|
|
277
|
+
/** Start with descending sort as first toggle state */
|
|
278
|
+
sortDescFirst?: boolean;
|
|
279
|
+
/** Controlled sort state (for server-side sorting) */
|
|
280
|
+
sortState?: SortingState;
|
|
281
|
+
/** Callback when sorting changes (for server-side sorting) */
|
|
282
|
+
onSortingChange?: (sorting: SortingState) => void;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Sorting state type - array of sort descriptors
|
|
286
|
+
*/
|
|
287
|
+
export type NvDataTableSortingState = SortingState;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable jsdoc/require-jsdoc */
|
|
2
2
|
import { defineComponent, computed, h, ref, watch, watchEffect, onUnmounted, } from 'vue';
|
|
3
|
-
import { useVueTable, getCoreRowModel, getPaginationRowModel, } from '@tanstack/vue-table';
|
|
4
|
-
import { NvTable } from '../generated/components';
|
|
3
|
+
import { useVueTable, getCoreRowModel, getPaginationRowModel, getSortedRowModel, } from '@tanstack/vue-table';
|
|
4
|
+
import { NvTable, NvTableheader } from '../generated/components';
|
|
5
5
|
/**
|
|
6
6
|
* Creates a typed NvDatatable component for a specific row type. This is the
|
|
7
7
|
* standard approach for generic components in Vue.
|
|
@@ -27,6 +27,10 @@ export function createNvDatatable() {
|
|
|
27
27
|
type: Object,
|
|
28
28
|
default: undefined,
|
|
29
29
|
},
|
|
30
|
+
sorting: {
|
|
31
|
+
type: Object,
|
|
32
|
+
default: undefined,
|
|
33
|
+
},
|
|
30
34
|
renderPagination: {
|
|
31
35
|
type: Function,
|
|
32
36
|
default: undefined,
|
|
@@ -42,69 +46,75 @@ export function createNvDatatable() {
|
|
|
42
46
|
pageIndex: 0,
|
|
43
47
|
pageSize: props.pagination?.initialPageSize || 10,
|
|
44
48
|
});
|
|
49
|
+
// Sorting state (controlled or uncontrolled)
|
|
50
|
+
const sortingState = ref(props.sorting?.sortState || []);
|
|
45
51
|
// Ref for observing last row (infinite scroll)
|
|
46
52
|
const lastRowRef = ref(null);
|
|
47
53
|
const tableColumns = computed(() => props.columns
|
|
48
54
|
.filter((col) => !col.hidden)
|
|
49
|
-
.map((col) =>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
// Client-side pagination needs direct config, server-side needs reactive config
|
|
92
|
-
let table;
|
|
93
|
-
if (!props.pagination || props.pagination.mode === 'infinite') {
|
|
94
|
-
// No pagination or infinite scroll - simple config
|
|
95
|
-
table = useVueTable({
|
|
96
|
-
get data() {
|
|
97
|
-
return computed(() => props.rows);
|
|
98
|
-
},
|
|
99
|
-
get columns() {
|
|
100
|
-
return tableColumns.value;
|
|
55
|
+
.map((col) => {
|
|
56
|
+
const columnDef = {
|
|
57
|
+
accessorKey: col.field,
|
|
58
|
+
accessorFn: col.valueFormatter
|
|
59
|
+
? (row) => {
|
|
60
|
+
const rawValue = row[col.field];
|
|
61
|
+
return col.valueFormatter({
|
|
62
|
+
value: rawValue,
|
|
63
|
+
row,
|
|
64
|
+
field: col.field,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
: undefined,
|
|
68
|
+
header: col.headerName || String(col.field),
|
|
69
|
+
size: col.width,
|
|
70
|
+
enableResizing: col.resizable ?? true,
|
|
71
|
+
// Sorting configuration
|
|
72
|
+
enableSorting: props.sorting ? col.sortable ?? true : false,
|
|
73
|
+
cell: (context) => {
|
|
74
|
+
const value = context.getValue();
|
|
75
|
+
const row = context.row.original;
|
|
76
|
+
const rowIndex = context.row.index;
|
|
77
|
+
const field = col.field;
|
|
78
|
+
// Priority: slot > renderCell > default
|
|
79
|
+
// Sanitize field name to ensure valid HTML attribute name
|
|
80
|
+
// Replace invalid characters (anything not a-z, A-Z, 0-9, -, _) with underscore
|
|
81
|
+
const sanitizedField = String(field).replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
82
|
+
const slotName = `cell-${sanitizedField}`;
|
|
83
|
+
if (slots[slotName]) {
|
|
84
|
+
return slots[slotName]({ value, row, field, rowIndex });
|
|
85
|
+
}
|
|
86
|
+
// Use custom renderCell if provided
|
|
87
|
+
if (col.renderCell) {
|
|
88
|
+
return col.renderCell({
|
|
89
|
+
value,
|
|
90
|
+
row,
|
|
91
|
+
field,
|
|
92
|
+
rowIndex,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// Default rendering
|
|
96
|
+
return value;
|
|
101
97
|
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
};
|
|
99
|
+
// Add optional sorting properties only if defined
|
|
100
|
+
if (col.sortingFn !== undefined) {
|
|
101
|
+
// @ts-expect-error - TanStack typing is strict but this works at runtime
|
|
102
|
+
columnDef.sortingFn = col.sortingFn;
|
|
103
|
+
}
|
|
104
|
+
if (col.sortDescFirst !== undefined) {
|
|
105
|
+
columnDef.sortDescFirst = col.sortDescFirst;
|
|
106
|
+
}
|
|
107
|
+
if (col.invertSorting !== undefined) {
|
|
108
|
+
columnDef.invertSorting = col.invertSorting;
|
|
109
|
+
}
|
|
110
|
+
if (col.sortUndefined !== undefined) {
|
|
111
|
+
columnDef.sortUndefined = col.sortUndefined;
|
|
112
|
+
}
|
|
113
|
+
return columnDef;
|
|
114
|
+
}));
|
|
115
|
+
// Determine base table configuration with sorting
|
|
116
|
+
const getBaseTableConfig = () => {
|
|
117
|
+
const baseConfig = {
|
|
108
118
|
get data() {
|
|
109
119
|
return computed(() => props.rows);
|
|
110
120
|
},
|
|
@@ -112,54 +122,103 @@ export function createNvDatatable() {
|
|
|
112
122
|
return tableColumns.value;
|
|
113
123
|
},
|
|
114
124
|
getCoreRowModel: getCoreRowModel(),
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
125
|
+
// Sorting configuration
|
|
126
|
+
...(props.sorting && {
|
|
127
|
+
get state() {
|
|
128
|
+
return {
|
|
129
|
+
sorting: props.sorting.mode === 'server' && props.sorting.sortState
|
|
130
|
+
? props.sorting.sortState
|
|
131
|
+
: sortingState.value,
|
|
132
|
+
};
|
|
120
133
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
onSortingChange: (updaterOrValue) => {
|
|
135
|
+
const currentSort = props.sorting.mode === 'server' && props.sorting.sortState
|
|
136
|
+
? props.sorting.sortState
|
|
137
|
+
: sortingState.value;
|
|
138
|
+
const newSort = typeof updaterOrValue === 'function'
|
|
139
|
+
? updaterOrValue(currentSort)
|
|
140
|
+
: updaterOrValue;
|
|
141
|
+
// Always update internal state for reactivity
|
|
142
|
+
sortingState.value = newSort;
|
|
143
|
+
// For server-side sorting, also call the callback
|
|
144
|
+
if (props.sorting?.mode === 'server' &&
|
|
145
|
+
props.sorting.onSortingChange) {
|
|
146
|
+
props.sorting.onSortingChange(newSort);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
manualSorting: props.sorting.mode === 'server',
|
|
150
|
+
enableSorting: true,
|
|
151
|
+
enableMultiSort: props.sorting.enableMultiSort ?? false,
|
|
152
|
+
enableSortingRemoval: props.sorting.enableSortingRemoval ?? true,
|
|
153
|
+
maxMultiSortColCount: props.sorting.maxMultiSortColCount,
|
|
154
|
+
sortDescFirst: props.sorting.sortDescFirst ?? false,
|
|
155
|
+
// When multi-sort is enabled, treat all clicks as multi-sort events
|
|
156
|
+
isMultiSortEvent: props.sorting.enableMultiSort
|
|
157
|
+
? () => true
|
|
158
|
+
: undefined,
|
|
159
|
+
getSortedRowModel: props.sorting.mode === 'client' ? getSortedRowModel() : undefined,
|
|
160
|
+
}),
|
|
161
|
+
};
|
|
162
|
+
return baseConfig;
|
|
163
|
+
};
|
|
164
|
+
// Create reactive table instance based on pagination mode
|
|
165
|
+
const table = computed(() => {
|
|
166
|
+
const baseConfig = getBaseTableConfig();
|
|
167
|
+
if (!props.pagination || props.pagination.mode === 'infinite') {
|
|
168
|
+
// No pagination or infinite scroll - simple config
|
|
169
|
+
return useVueTable(baseConfig);
|
|
170
|
+
}
|
|
171
|
+
else if (props.pagination.mode === 'client') {
|
|
172
|
+
// Client-side pagination - table manages its own state
|
|
173
|
+
return useVueTable({
|
|
174
|
+
...baseConfig,
|
|
175
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
176
|
+
initialState: {
|
|
177
|
+
pagination: {
|
|
178
|
+
pageIndex: 0,
|
|
179
|
+
pageSize: props.pagination.initialPageSize || 10,
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
// Server-side pagination - manual pagination with reactive state
|
|
186
|
+
return useVueTable({
|
|
187
|
+
...baseConfig,
|
|
188
|
+
manualPagination: true,
|
|
189
|
+
get pageCount() {
|
|
190
|
+
if (!props.pagination || props.pagination.mode !== 'server') {
|
|
191
|
+
return -1;
|
|
192
|
+
}
|
|
193
|
+
const pageSize = paginationState.value.pageSize;
|
|
194
|
+
if (props.pagination.totalPageCount !== undefined) {
|
|
195
|
+
return props.pagination.totalPageCount;
|
|
196
|
+
}
|
|
197
|
+
else if (props.pagination.totalRowCount !== undefined) {
|
|
198
|
+
return Math.ceil(props.pagination.totalRowCount / pageSize);
|
|
199
|
+
}
|
|
137
200
|
return -1;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
}
|
|
201
|
+
},
|
|
202
|
+
get state() {
|
|
203
|
+
const sortState = baseConfig.state
|
|
204
|
+
? baseConfig.state.sorting
|
|
205
|
+
: undefined;
|
|
206
|
+
return {
|
|
207
|
+
pagination: paginationState.value,
|
|
208
|
+
...(sortState !== undefined && { sorting: sortState }),
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
onPaginationChange: (updaterOrValue) => {
|
|
212
|
+
if (typeof updaterOrValue === 'function') {
|
|
213
|
+
paginationState.value = updaterOrValue(paginationState.value);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
paginationState.value = updaterOrValue;
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
163
222
|
// Handle pagination changes for server mode
|
|
164
223
|
watch(paginationState, (newState) => {
|
|
165
224
|
if (props.pagination?.mode === 'server' &&
|
|
@@ -215,8 +274,8 @@ export function createNvDatatable() {
|
|
|
215
274
|
if (!props.pagination) {
|
|
216
275
|
return null;
|
|
217
276
|
}
|
|
218
|
-
const tablePaginationState = table.getState().pagination;
|
|
219
|
-
const pageCount = table.getPageCount();
|
|
277
|
+
const tablePaginationState = table.value.getState().pagination;
|
|
278
|
+
const pageCount = table.value.getPageCount();
|
|
220
279
|
const rowCount = props.pagination.mode === 'server'
|
|
221
280
|
? props.pagination.totalRowCount || props.rows.length
|
|
222
281
|
: props.rows.length;
|
|
@@ -225,14 +284,14 @@ export function createNvDatatable() {
|
|
|
225
284
|
pageSize: tablePaginationState.pageSize,
|
|
226
285
|
pageCount,
|
|
227
286
|
rowCount,
|
|
228
|
-
firstPage: () => table.setPageIndex(0),
|
|
229
|
-
previousPage: () => table.previousPage(),
|
|
230
|
-
nextPage: () => table.nextPage(),
|
|
231
|
-
lastPage: () => table.setPageIndex(pageCount - 1),
|
|
232
|
-
setPageIndex: (index) => table.setPageIndex(index),
|
|
233
|
-
setPageSize: (size) => table.setPageSize(size),
|
|
234
|
-
canPreviousPage: table.getCanPreviousPage(),
|
|
235
|
-
canNextPage: table.getCanNextPage(),
|
|
287
|
+
firstPage: () => table.value.setPageIndex(0),
|
|
288
|
+
previousPage: () => table.value.previousPage(),
|
|
289
|
+
nextPage: () => table.value.nextPage(),
|
|
290
|
+
lastPage: () => table.value.setPageIndex(pageCount - 1),
|
|
291
|
+
setPageIndex: (index) => table.value.setPageIndex(index),
|
|
292
|
+
setPageSize: (size) => table.value.setPageSize(size),
|
|
293
|
+
canPreviousPage: table.value.getCanPreviousPage(),
|
|
294
|
+
canNextPage: table.value.getCanNextPage(),
|
|
236
295
|
isLoading: props.pagination.mode === 'infinite'
|
|
237
296
|
? props.pagination.isLoading
|
|
238
297
|
: undefined,
|
|
@@ -242,7 +301,7 @@ export function createNvDatatable() {
|
|
|
242
301
|
};
|
|
243
302
|
});
|
|
244
303
|
return () => {
|
|
245
|
-
const tableRows = table.getRowModel().rows;
|
|
304
|
+
const tableRows = table.value.getRowModel().rows;
|
|
246
305
|
const isInfiniteScroll = props.pagination?.mode === 'infinite';
|
|
247
306
|
const tableElement = h(NvTable, attrs, {
|
|
248
307
|
default: () => [
|
|
@@ -250,25 +309,44 @@ export function createNvDatatable() {
|
|
|
250
309
|
h('thead', {
|
|
251
310
|
'data-sticky-top': props.stickyHeader ? 'true' : undefined,
|
|
252
311
|
}, [
|
|
253
|
-
...table.getHeaderGroups().map((headerGroup) => h('tr', { key: headerGroup.id }, [
|
|
254
|
-
...headerGroup.headers.map((header) =>
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
312
|
+
...table.value.getHeaderGroups().map((headerGroup) => h('tr', { key: headerGroup.id }, [
|
|
313
|
+
...headerGroup.headers.map((header) => {
|
|
314
|
+
const canSort = header.column.getCanSort();
|
|
315
|
+
const sortDirection = header.column.getIsSorted();
|
|
316
|
+
return h('th', {
|
|
317
|
+
key: header.id,
|
|
318
|
+
'data-testid': `datatable-header-${header.id}`,
|
|
319
|
+
style: {
|
|
320
|
+
width: header.column.columnDef.size
|
|
321
|
+
? `${header.column.columnDef.size}px`
|
|
322
|
+
: undefined,
|
|
323
|
+
},
|
|
324
|
+
'data-no-resize': header.column.columnDef
|
|
325
|
+
.enableResizing
|
|
326
|
+
? null
|
|
327
|
+
: true,
|
|
328
|
+
}, header.isPlaceholder
|
|
264
329
|
? null
|
|
265
|
-
:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
330
|
+
: canSort
|
|
331
|
+
? h(NvTableheader, {
|
|
332
|
+
sortable: true,
|
|
333
|
+
sortDirection: sortDirection || 'none',
|
|
334
|
+
onSortDirectionChanged: (event) => {
|
|
335
|
+
// Call the TanStack handler
|
|
336
|
+
const handler = header.column.getToggleSortingHandler();
|
|
337
|
+
handler?.(event);
|
|
338
|
+
},
|
|
339
|
+
}, {
|
|
340
|
+
default: () => typeof header.column.columnDef.header ===
|
|
341
|
+
'function'
|
|
342
|
+
? header.column.columnDef.header(header.getContext())
|
|
343
|
+
: header.column.columnDef.header,
|
|
344
|
+
})
|
|
345
|
+
: typeof header.column.columnDef.header ===
|
|
346
|
+
'function'
|
|
347
|
+
? header.column.columnDef.header(header.getContext())
|
|
348
|
+
: header.column.columnDef.header);
|
|
349
|
+
}),
|
|
272
350
|
])),
|
|
273
351
|
]),
|
|
274
352
|
h('tbody', {}, [
|
|
@@ -276,12 +354,12 @@ export function createNvDatatable() {
|
|
|
276
354
|
const isLastRow = isInfiniteScroll && index === tableRows.length - 1;
|
|
277
355
|
return h('tr', {
|
|
278
356
|
key: row.id,
|
|
279
|
-
'data-testid': `datatable-row-${
|
|
357
|
+
'data-testid': `datatable-row-${index}`,
|
|
280
358
|
ref: isLastRow ? lastRowRef : undefined,
|
|
281
359
|
}, [
|
|
282
360
|
...row.getVisibleCells().map((cell) => h('td', {
|
|
283
361
|
key: cell.id,
|
|
284
|
-
'data-testid': `datatable-cell-${cell.id}`,
|
|
362
|
+
'data-testid': `datatable-cell-${cell.column.id}`,
|
|
285
363
|
}, typeof cell.column.columnDef.cell === 'function'
|
|
286
364
|
? cell.column.columnDef.cell(cell.getContext())
|
|
287
365
|
: cell.getValue())),
|
|
@@ -56,12 +56,23 @@ export declare const NvLoader: import("vue").DefineSetupFnComponent<JSX.NvLoader
|
|
|
56
56
|
export declare const NvMenu: import("vue").DefineSetupFnComponent<JSX.NvMenu & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvMenu & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
57
57
|
export declare const NvMenuitem: import("vue").DefineSetupFnComponent<JSX.NvMenuitem & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvMenuitem & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
58
58
|
export declare const NvNotification: import("vue").DefineSetupFnComponent<JSX.NvNotification & import("./vue-component-lib/utils").InputProps<boolean>, {}, {}, JSX.NvNotification & import("./vue-component-lib/utils").InputProps<boolean> & {}, import("vue").PublicProps>;
|
|
59
|
+
export declare const NvNotificationBullet: import("vue").DefineSetupFnComponent<JSX.NvNotificationBullet & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvNotificationBullet & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
59
60
|
export declare const NvNotificationcontainer: import("vue").DefineSetupFnComponent<JSX.NvNotificationcontainer & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvNotificationcontainer & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
60
61
|
export declare const NvPopover: import("vue").DefineSetupFnComponent<JSX.NvPopover & import("./vue-component-lib/utils").InputProps<boolean>, {}, {}, JSX.NvPopover & import("./vue-component-lib/utils").InputProps<boolean> & {}, import("vue").PublicProps>;
|
|
61
62
|
export declare const NvRow: import("vue").DefineSetupFnComponent<JSX.NvRow & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvRow & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
63
|
+
export declare const NvSidebar: import("vue").DefineSetupFnComponent<JSX.NvSidebar & import("./vue-component-lib/utils").InputProps<boolean>, {}, {}, JSX.NvSidebar & import("./vue-component-lib/utils").InputProps<boolean> & {}, import("vue").PublicProps>;
|
|
64
|
+
export declare const NvSidebarcontent: import("vue").DefineSetupFnComponent<JSX.NvSidebarcontent & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvSidebarcontent & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
65
|
+
export declare const NvSidebardivider: import("vue").DefineSetupFnComponent<JSX.NvSidebardivider & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvSidebardivider & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
66
|
+
export declare const NvSidebarfooter: import("vue").DefineSetupFnComponent<JSX.NvSidebarfooter & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvSidebarfooter & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
67
|
+
export declare const NvSidebargroup: import("vue").DefineSetupFnComponent<JSX.NvSidebargroup & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvSidebargroup & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
68
|
+
export declare const NvSidebarheader: import("vue").DefineSetupFnComponent<JSX.NvSidebarheader & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvSidebarheader & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
69
|
+
export declare const NvSidebarlogo: import("vue").DefineSetupFnComponent<JSX.NvSidebarlogo & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvSidebarlogo & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
70
|
+
export declare const NvSidebarnavitem: import("vue").DefineSetupFnComponent<JSX.NvSidebarnavitem & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvSidebarnavitem & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
71
|
+
export declare const NvSidebarnavsubitem: import("vue").DefineSetupFnComponent<JSX.NvSidebarnavsubitem & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvSidebarnavsubitem & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
62
72
|
export declare const NvSplit: import("vue").DefineSetupFnComponent<JSX.NvSplit & import("./vue-component-lib/utils").InputProps<number[]>, {}, {}, JSX.NvSplit & import("./vue-component-lib/utils").InputProps<number[]> & {}, import("vue").PublicProps>;
|
|
63
73
|
export declare const NvStack: import("vue").DefineSetupFnComponent<JSX.NvStack & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvStack & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
64
74
|
export declare const NvTable: import("vue").DefineSetupFnComponent<JSX.NvTable & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvTable & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
75
|
+
export declare const NvTableheader: import("vue").DefineSetupFnComponent<JSX.NvTableheader & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvTableheader & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
65
76
|
export declare const NvToggle: import("vue").DefineSetupFnComponent<JSX.NvToggle & import("./vue-component-lib/utils").InputProps<boolean>, {}, {}, JSX.NvToggle & import("./vue-component-lib/utils").InputProps<boolean> & {}, import("vue").PublicProps>;
|
|
66
77
|
export declare const NvTogglebutton: import("vue").DefineSetupFnComponent<JSX.NvTogglebutton & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvTogglebutton & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
67
78
|
export declare const NvTogglebuttongroup: import("vue").DefineSetupFnComponent<JSX.NvTogglebuttongroup & import("./vue-component-lib/utils").InputProps<string[]>, {}, {}, JSX.NvTogglebuttongroup & import("./vue-component-lib/utils").InputProps<string[]> & {}, import("vue").PublicProps>;
|
|
@@ -75,7 +75,8 @@ export const NvButton = /*@__PURE__*/ defineContainer('nv-button', undefined, [
|
|
|
75
75
|
'disabled',
|
|
76
76
|
'fluid',
|
|
77
77
|
'type',
|
|
78
|
-
'form'
|
|
78
|
+
'form',
|
|
79
|
+
'disableTabindex'
|
|
79
80
|
]);
|
|
80
81
|
export const NvButtongroup = /*@__PURE__*/ defineContainer('nv-buttongroup', undefined, [
|
|
81
82
|
'size',
|
|
@@ -515,7 +516,8 @@ export const NvIconbutton = /*@__PURE__*/ defineContainer('nv-iconbutton', undef
|
|
|
515
516
|
'active',
|
|
516
517
|
'name',
|
|
517
518
|
'type',
|
|
518
|
-
'shape'
|
|
519
|
+
'shape',
|
|
520
|
+
'disableTabindex'
|
|
519
521
|
]);
|
|
520
522
|
export const NvLoader = /*@__PURE__*/ defineContainer('nv-loader', undefined, [
|
|
521
523
|
'size',
|
|
@@ -550,6 +552,13 @@ export const NvNotification = /*@__PURE__*/ defineContainer('nv-notification', u
|
|
|
550
552
|
'initiallyHidden',
|
|
551
553
|
'hiddenChanged'
|
|
552
554
|
], 'hidden', 'hidden-changed');
|
|
555
|
+
export const NvNotificationBullet = /*@__PURE__*/ defineContainer('nv-notification-bullet', undefined, [
|
|
556
|
+
'count',
|
|
557
|
+
'intention',
|
|
558
|
+
'emphasis',
|
|
559
|
+
'size',
|
|
560
|
+
'contrastingBorder'
|
|
561
|
+
]);
|
|
553
562
|
export const NvNotificationcontainer = /*@__PURE__*/ defineContainer('nv-notificationcontainer', undefined, [
|
|
554
563
|
'position'
|
|
555
564
|
]);
|
|
@@ -569,6 +578,36 @@ export const NvPopover = /*@__PURE__*/ defineContainer('nv-popover', undefined,
|
|
|
569
578
|
'openChanged'
|
|
570
579
|
], 'open', 'open-changed');
|
|
571
580
|
export const NvRow = /*@__PURE__*/ defineContainer('nv-row', undefined);
|
|
581
|
+
export const NvSidebar = /*@__PURE__*/ defineContainer('nv-sidebar', undefined, [
|
|
582
|
+
'type',
|
|
583
|
+
'open',
|
|
584
|
+
'activePath',
|
|
585
|
+
'notificationIntention',
|
|
586
|
+
'notificationEmphasis',
|
|
587
|
+
'openChanged'
|
|
588
|
+
], 'open', 'open-changed');
|
|
589
|
+
export const NvSidebarcontent = /*@__PURE__*/ defineContainer('nv-sidebarcontent', undefined);
|
|
590
|
+
export const NvSidebardivider = /*@__PURE__*/ defineContainer('nv-sidebardivider', undefined);
|
|
591
|
+
export const NvSidebarfooter = /*@__PURE__*/ defineContainer('nv-sidebarfooter', undefined);
|
|
592
|
+
export const NvSidebargroup = /*@__PURE__*/ defineContainer('nv-sidebargroup', undefined, [
|
|
593
|
+
'label'
|
|
594
|
+
]);
|
|
595
|
+
export const NvSidebarheader = /*@__PURE__*/ defineContainer('nv-sidebarheader', undefined);
|
|
596
|
+
export const NvSidebarlogo = /*@__PURE__*/ defineContainer('nv-sidebarlogo', undefined, [
|
|
597
|
+
'label',
|
|
598
|
+
'logo',
|
|
599
|
+
'collapsedLogo'
|
|
600
|
+
]);
|
|
601
|
+
export const NvSidebarnavitem = /*@__PURE__*/ defineContainer('nv-sidebarnavitem', undefined, [
|
|
602
|
+
'icon',
|
|
603
|
+
'active',
|
|
604
|
+
'collapsible',
|
|
605
|
+
'open',
|
|
606
|
+
'notificationCount'
|
|
607
|
+
]);
|
|
608
|
+
export const NvSidebarnavsubitem = /*@__PURE__*/ defineContainer('nv-sidebarnavsubitem', undefined, [
|
|
609
|
+
'active'
|
|
610
|
+
]);
|
|
572
611
|
export const NvSplit = /*@__PURE__*/ defineContainer('nv-split', undefined, [
|
|
573
612
|
'direction',
|
|
574
613
|
'sizes',
|
|
@@ -584,6 +623,11 @@ export const NvStack = /*@__PURE__*/ defineContainer('nv-stack', undefined, [
|
|
|
584
623
|
'vertical'
|
|
585
624
|
]);
|
|
586
625
|
export const NvTable = /*@__PURE__*/ defineContainer('nv-table', undefined);
|
|
626
|
+
export const NvTableheader = /*@__PURE__*/ defineContainer('nv-tableheader', undefined, [
|
|
627
|
+
'sortable',
|
|
628
|
+
'sortDirection',
|
|
629
|
+
'sortDirectionChanged'
|
|
630
|
+
]);
|
|
587
631
|
export const NvToggle = /*@__PURE__*/ defineContainer('nv-toggle', undefined, [
|
|
588
632
|
'inputId',
|
|
589
633
|
'name',
|
|
@@ -33,6 +33,8 @@ export interface NotificationOptions {
|
|
|
33
33
|
actions?: NotificationAction[];
|
|
34
34
|
/** Custom components for the notification actions. */
|
|
35
35
|
actionSlot?: Component;
|
|
36
|
+
/** Duration in milliseconds before auto-dismissing. 0 = sticky (no auto-dismiss). Default: 0 */
|
|
37
|
+
duration?: number;
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
40
|
* A notification with all required fields populated.
|
|
@@ -25,6 +25,7 @@ class NotificationManager {
|
|
|
25
25
|
notifications = ref([]);
|
|
26
26
|
options;
|
|
27
27
|
containerApp = null;
|
|
28
|
+
timers = new Map();
|
|
28
29
|
constructor(options = {}) {
|
|
29
30
|
this.options = {
|
|
30
31
|
position: options.position || 'top-right',
|
|
@@ -116,6 +117,7 @@ class NotificationManager {
|
|
|
116
117
|
actions: options.actions ?? [],
|
|
117
118
|
actionSlot: options.actionSlot,
|
|
118
119
|
icon: options.icon,
|
|
120
|
+
duration: options.duration ?? 0,
|
|
119
121
|
createdAt: Date.now(),
|
|
120
122
|
};
|
|
121
123
|
// Remove oldest notifications if we exceed max
|
|
@@ -130,6 +132,13 @@ class NotificationManager {
|
|
|
130
132
|
const ref = this.elRefs.get(id);
|
|
131
133
|
const el = unwrapNotificationEl(ref);
|
|
132
134
|
el?.show();
|
|
135
|
+
// Set up auto-dismiss timer if duration > 0
|
|
136
|
+
if (notification.duration && notification.duration > 0) {
|
|
137
|
+
const timer = setTimeout(() => {
|
|
138
|
+
this.dismiss(id);
|
|
139
|
+
}, notification.duration);
|
|
140
|
+
this.timers.set(id, timer);
|
|
141
|
+
}
|
|
133
142
|
}, 0);
|
|
134
143
|
return id;
|
|
135
144
|
};
|
|
@@ -140,6 +149,12 @@ class NotificationManager {
|
|
|
140
149
|
* @param {string} id - The notification ID to dismiss
|
|
141
150
|
*/
|
|
142
151
|
dismiss = (id) => {
|
|
152
|
+
// Clear timer if exists
|
|
153
|
+
const timer = this.timers.get(id);
|
|
154
|
+
if (timer) {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
this.timers.delete(id);
|
|
157
|
+
}
|
|
143
158
|
const ref = this.elRefs.get(id);
|
|
144
159
|
const el = unwrapNotificationEl(ref);
|
|
145
160
|
el?.dismiss();
|
|
@@ -150,6 +165,12 @@ class NotificationManager {
|
|
|
150
165
|
* @param {string} id - The notification ID to dismiss
|
|
151
166
|
*/
|
|
152
167
|
remove = (id) => {
|
|
168
|
+
// Clear timer if exists
|
|
169
|
+
const timer = this.timers.get(id);
|
|
170
|
+
if (timer) {
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
this.timers.delete(id);
|
|
173
|
+
}
|
|
153
174
|
this.notifications.value = this.notifications.value.filter((notification) => notification.id !== id);
|
|
154
175
|
this.elRefs.delete(id);
|
|
155
176
|
};
|
|
@@ -157,6 +178,9 @@ class NotificationManager {
|
|
|
157
178
|
* Clear all active notifications.
|
|
158
179
|
*/
|
|
159
180
|
removeAll = () => {
|
|
181
|
+
// Clear all timers
|
|
182
|
+
this.timers.forEach((timer) => clearTimeout(timer));
|
|
183
|
+
this.timers.clear();
|
|
160
184
|
this.notifications.value = [];
|
|
161
185
|
};
|
|
162
186
|
/**
|
|
@@ -175,6 +199,9 @@ class NotificationManager {
|
|
|
175
199
|
* Destroy the notification manager and clean up resources.
|
|
176
200
|
*/
|
|
177
201
|
destroy() {
|
|
202
|
+
// Clear all timers
|
|
203
|
+
this.timers.forEach((timer) => clearTimeout(timer));
|
|
204
|
+
this.timers.clear();
|
|
178
205
|
if (this.containerApp) {
|
|
179
206
|
this.containerApp.unmount();
|
|
180
207
|
this.containerApp = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nova-design-system/nova-vue",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.21.1-beta.0",
|
|
4
4
|
"description": "Nova is a design system created by Elia Group to empower creators to efficiently build solutions that people love to use.",
|
|
5
5
|
"author": "Elia Group",
|
|
6
6
|
"homepage": "https://nova.eliagroup.io",
|