@nova-design-system/nova-vue 3.12.0 → 3.14.0-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 +49 -101
- package/dist/generated/components.d.ts +2 -0
- package/dist/generated/components.js +17 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugin.d.ts +14 -4
- package/dist/plugin.js +7 -3
- package/dist/providers/NotificationService.d.ts +84 -0
- package/dist/providers/NotificationService.js +215 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +1 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -5,20 +5,18 @@
|
|
|
5
5
|
- [Nova Components Vue](#nova-components-vue)
|
|
6
6
|
- [Key Features](#key-features)
|
|
7
7
|
- [Installation](#installation)
|
|
8
|
-
- [
|
|
8
|
+
- [Setting up Tailwind](#setting-up-tailwind)
|
|
9
|
+
- [About Tailwind and the Nova Plugin](#about-tailwind-and-the-nova-plugin)
|
|
9
10
|
- [1. Install Tailwind CSS and the Vite Plugin](#1-install-tailwind-css-and-the-vite-plugin)
|
|
10
11
|
- [2. Configure the Vite Plugin](#2-configure-the-vite-plugin)
|
|
11
12
|
- [3. Create `tailwind.config.ts`](#3-create-tailwindconfigts)
|
|
12
13
|
- [4. Configure Tailwind and Nova Plugin in `main.css`](#4-configure-tailwind-and-nova-plugin-in-maincss)
|
|
13
14
|
- [5. Register NovaComponents and include the Nova Tokens](#5-register-novacomponents-and-include-the-nova-tokens)
|
|
14
|
-
- [6. Use Nova Components
|
|
15
|
+
- [6. Use Nova Components with Tailwind Utilities](#6-use-nova-components-with-tailwind-utilities)
|
|
15
16
|
- [7. Setup the Nova Font](#7-setup-the-nova-font)
|
|
16
17
|
- [Creating Your Own Style Components with Tailwind](#creating-your-own-style-components-with-tailwind)
|
|
17
|
-
- [Setup Without Tailwind (Not Recommended)](#setup-without-tailwind-not-recommended)
|
|
18
|
-
- [1. Register NovaComponents and include the Nova CSS.](#1-register-novacomponents-and-include-the-nova-css)
|
|
19
|
-
- [2. Use Nova Components](#2-use-nova-components)
|
|
20
18
|
- [Nova Font Pro Integration](#nova-font-pro-integration)
|
|
21
|
-
- [Option 1: Import in
|
|
19
|
+
- [Option 1: Import in Global CSS (Recommended)](#option-1-import-in-global-css-recommended)
|
|
22
20
|
- [Option 2: HTML Integration](#option-2-html-integration)
|
|
23
21
|
|
|
24
22
|
|
|
@@ -27,7 +25,7 @@
|
|
|
27
25
|
## Key Features
|
|
28
26
|
|
|
29
27
|
- **Lightweight Integration**: Leverage Nova Web Components with minimal configuration in Vue.
|
|
30
|
-
- **Customizable Styling**: Use Tailwind
|
|
28
|
+
- **Customizable Styling**: Use Tailwind’s utility classes with the Nova Tailwind theme and plugin for token-driven styling and layouts.
|
|
31
29
|
- **Dark Mode Ready**: Toggle dark mode by adding the `dark` class to your `body` element.
|
|
32
30
|
- **Nova Font Pro Support**: Easily integrate Nova’s custom font for a consistent design experience.
|
|
33
31
|
|
|
@@ -48,19 +46,31 @@ yarn add @nova-design-system/nova-webcomponents @nova-design-system/nova-base @n
|
|
|
48
46
|
```
|
|
49
47
|
|
|
50
48
|
> In some case, you might experience SSL certificate issues when working on Developers' VM. As documented in the [Developers' setup guide](https://wiki.eliagroup.eu/spaces/EAing/pages/89296007/2.3.3.10+Developer+Setup#id-2.3.3.10DeveloperSetup-NPMconfig), you need to turn off the SSL certificate verification:
|
|
49
|
+
>
|
|
50
|
+
> ```bash
|
|
51
|
+
> npm config set strict-ssl false
|
|
52
|
+
> ```
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
npm config set strict-ssl false
|
|
54
|
-
```
|
|
54
|
+
---
|
|
55
55
|
|
|
56
|
-
##
|
|
56
|
+
## Setting up Tailwind
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
Nova Vue requires Tailwind CSS for styling. Tailwind provides a powerful utility-first workflow and an optimized bundle size. Nova includes a dedicated Tailwind theme and plugin that map Nova’s design tokens to Tailwind’s theme and utilities, enabling consistent, token-driven styling across your app.
|
|
59
59
|
|
|
60
60
|
> **Tailwind Version**
|
|
61
61
|
> This guide is written for Tailwind v4. While compatible with v3, some features may not work as expected.
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
### About Tailwind and the Nova Plugin
|
|
64
|
+
|
|
65
|
+
- **What is Tailwind?** A utility-first CSS framework with low-level, composable classes (flex, grid, spacing, color, typography) to rapidly build UIs.
|
|
66
|
+
- **Nova Tokens**: Nova ships design tokens as CSS variables (via the Spark and Ocean themes) covering colors, spacing, typography, radii, shadows, and more.
|
|
67
|
+
- **Integration**:
|
|
68
|
+
- `novaTailwindTheme` wires Nova tokens into Tailwind’s theme scales.
|
|
69
|
+
- The Nova Tailwind plugin exposes utilities and variants that reference those tokens, so your Tailwind classes resolve to Nova’s token values at runtime.
|
|
70
|
+
- **Why import tokens CSS?** Import one token CSS file (`spark.css` or `ocean.css`) so the underlying CSS variables exist at runtime. The Tailwind utilities generated by the plugin read from these variables.
|
|
71
|
+
- **Do not mix with legacy utilities**: When using Tailwind, do not import `@nova-design-system/nova-base/dist/css/nova-utils.css` to avoid redundant CSS and larger bundles.
|
|
72
|
+
|
|
73
|
+
Below is an example setup using the **Vue CLI with Vite**. If you're using another framework, such as Nuxt, please refer to the [Tailwind Installation Guide](https://tailwindcss.com/docs/installation).
|
|
64
74
|
|
|
65
75
|
### 1. Install Tailwind CSS and the Vite Plugin
|
|
66
76
|
|
|
@@ -92,7 +102,6 @@ export default defineConfig({
|
|
|
92
102
|
},
|
|
93
103
|
},
|
|
94
104
|
})
|
|
95
|
-
|
|
96
105
|
```
|
|
97
106
|
|
|
98
107
|
### 3. Create `tailwind.config.ts`
|
|
@@ -110,7 +119,7 @@ export default {
|
|
|
110
119
|
|
|
111
120
|
### 4. Configure Tailwind and Nova Plugin in `main.css`
|
|
112
121
|
|
|
113
|
-
|
|
122
|
+
In `src/assets/main.css`:
|
|
114
123
|
|
|
115
124
|
```css
|
|
116
125
|
@import 'tailwindcss';
|
|
@@ -125,18 +134,18 @@ in `src/assets/main.css`:
|
|
|
125
134
|
> **Dark Mode**
|
|
126
135
|
> To enable dark mode, add the `dark` class to the `<body>` element.
|
|
127
136
|
|
|
128
|
-
|
|
129
137
|
### 5. Register NovaComponents and include the Nova Tokens
|
|
130
|
-
Register the Nova Components Vue plugin in your `main.ts` file, and include the nova tokens (Spark or Ocean theme) css file:
|
|
131
138
|
|
|
132
|
-
|
|
139
|
+
Register the Nova Components Vue plugin in your `main.ts` file, and include exactly one of the Nova tokens (Spark or Ocean) CSS files:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
133
142
|
import './assets/main.css'
|
|
134
|
-
import '@nova-design-system/nova-base/dist/css/spark.css'
|
|
143
|
+
import '@nova-design-system/nova-base/dist/css/spark.css' // or ocean.css
|
|
135
144
|
|
|
136
145
|
import { createApp } from 'vue'
|
|
137
146
|
import App from './App.vue'
|
|
138
147
|
import router from './router'
|
|
139
|
-
import { NovaComponents } from '@nova-design-system/nova-vue/plugin'
|
|
148
|
+
import { NovaComponents } from '@nova-design-system/nova-vue/plugin'
|
|
140
149
|
|
|
141
150
|
const app = createApp(App)
|
|
142
151
|
|
|
@@ -146,14 +155,16 @@ app.use(NovaComponents)
|
|
|
146
155
|
app.mount('#app')
|
|
147
156
|
```
|
|
148
157
|
|
|
149
|
-
|
|
158
|
+
> When using Tailwind, do not import `@nova-design-system/nova-base/dist/css/nova-utils.css`.
|
|
150
159
|
|
|
151
|
-
|
|
160
|
+
### 6. Use Nova Components with Tailwind Utilities
|
|
161
|
+
|
|
162
|
+
```vue
|
|
152
163
|
<script setup lang="ts">
|
|
153
|
-
import { ref } from 'vue'
|
|
164
|
+
import { ref } from 'vue'
|
|
154
165
|
import { NvButton } from '@nova-design-system/nova-vue'
|
|
155
166
|
|
|
156
|
-
const count = ref(0)
|
|
167
|
+
const count = ref(0)
|
|
157
168
|
</script>
|
|
158
169
|
|
|
159
170
|
<template>
|
|
@@ -165,7 +176,7 @@ const count = ref(0);
|
|
|
165
176
|
</template>
|
|
166
177
|
```
|
|
167
178
|
|
|
168
|
-
> Note: **We have full
|
|
179
|
+
> Note: **We have full TypeScript and IntelliSense support for Nova components.** If you do not see autocomplete options, uninstall Volar/Vetur and use the [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) extension only.
|
|
169
180
|
|
|
170
181
|
### 7. Setup the Nova Font
|
|
171
182
|
|
|
@@ -186,23 +197,23 @@ If you find you’re repeating the same set of utility classes for certain UI el
|
|
|
186
197
|
|
|
187
198
|
Then in your markup, instead of:
|
|
188
199
|
|
|
189
|
-
```
|
|
200
|
+
```html
|
|
190
201
|
<div class="bg-gray-50 dark:bg-gray-500 p-4 rounded-md shadow-sm">
|
|
191
|
-
|
|
202
|
+
<!-- Content -->
|
|
192
203
|
</div>
|
|
193
204
|
<div class="bg-gray-50 dark:bg-gray-500 p-4 rounded-md shadow-sm">
|
|
194
|
-
|
|
205
|
+
<!-- Content -->
|
|
195
206
|
</div>
|
|
196
207
|
```
|
|
197
208
|
|
|
198
209
|
You can use your new `card` class:
|
|
199
210
|
|
|
200
|
-
```
|
|
211
|
+
```html
|
|
201
212
|
<div class="card">
|
|
202
|
-
|
|
213
|
+
<!-- Content -->
|
|
203
214
|
</div>
|
|
204
215
|
<div class="card">
|
|
205
|
-
|
|
216
|
+
<!-- Content -->
|
|
206
217
|
</div>
|
|
207
218
|
```
|
|
208
219
|
|
|
@@ -210,82 +221,19 @@ This ensures consistent styling and keeps your markup clean. Any colors or spaci
|
|
|
210
221
|
|
|
211
222
|
---
|
|
212
223
|
|
|
213
|
-
## Setup Without Tailwind (Not Recommended)
|
|
214
|
-
|
|
215
|
-
If you don’t plan to use Tailwind, Nova provides a large utility CSS file for quick prototyping. Be aware that this approach will increase your CSS bundle size, offer less options, and lacks the flexibility and optimizations of Tailwind.
|
|
216
|
-
|
|
217
|
-
### 1. Register NovaComponents and include the Nova CSS.
|
|
218
|
-
|
|
219
|
-
After installing the nova packages, you'll need to include the CSS for the theme and the utils. Then you register the Nova Components Vue plugin.
|
|
220
|
-
|
|
221
|
-
In your `main.ts` file:
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
import './assets/main.css'
|
|
225
|
-
import '@nova-design-system/nova-base/dist/css/nova-utils.css';
|
|
226
|
-
import '@nova-design-system/nova-base/dist/css/spark.css'; // or ocean.css
|
|
227
|
-
|
|
228
|
-
import { createApp } from 'vue'
|
|
229
|
-
import App from './App.vue'
|
|
230
|
-
import router from './router'
|
|
231
|
-
import { NovaComponents } from '@nova-design-system/nova-vue/plugin';
|
|
232
|
-
|
|
233
|
-
const app = createApp(App)
|
|
234
|
-
|
|
235
|
-
app.use(router)
|
|
236
|
-
app.use(NovaComponents)
|
|
237
|
-
|
|
238
|
-
app.mount('#app')
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### 2. Use Nova Components
|
|
242
|
-
|
|
243
|
-
```html
|
|
244
|
-
<script setup lang="ts">
|
|
245
|
-
import { ref } from 'vue';
|
|
246
|
-
import { NvButton } from '@nova-design-system/nova-vue'
|
|
247
|
-
|
|
248
|
-
const count = ref(0);
|
|
249
|
-
</script>
|
|
250
|
-
|
|
251
|
-
<template>
|
|
252
|
-
<div class="flex items-center justify-center">
|
|
253
|
-
<NvButton danger @click="count++">
|
|
254
|
-
Count is {{ count }}
|
|
255
|
-
</NvButton>
|
|
256
|
-
</div>
|
|
257
|
-
</template>
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
224
|
## Nova Font Pro Integration
|
|
263
225
|
|
|
264
226
|
> [!WARNING]
|
|
265
227
|
> Nova Fonts is a protected asset and is not included in the Nova Base package. You need to include the Nova Fonts CSS file in your project.
|
|
266
|
-
> To get the Nova Fonts URL, please contact us via Teams or get the URL in the Nova Design System [internal wiki](https://dev.azure.com/elia-digitization/Nova/_wiki/wikis/Nova.wiki/30245/Nova-Font-Pro).
|
|
267
|
-
|
|
268
|
-
Once you have the URL, you can integrate it using any of these methods:
|
|
269
|
-
|
|
270
|
-
### Option 1: Import in Main Entry (Recommended)
|
|
271
|
-
In your `main.ts`:
|
|
228
|
+
> To get the Nova Fonts URL, **please contact us via Teams** or get the URL in the Nova Design System [internal wiki](https://dev.azure.com/elia-digitization/Nova/_wiki/wikis/Nova.wiki/30245/Nova-Font-Pro).
|
|
272
229
|
|
|
273
|
-
|
|
274
|
-
import './assets/main.css'
|
|
275
|
-
import '@nova-design-system/nova-base/dist/css/spark.css'; // or ocean.css
|
|
276
|
-
import 'https://novaassets.azureedge.net/fonts/nova-fonts-pro.css';
|
|
230
|
+
Once you have the URL, you can integrate it using any of these methods:
|
|
277
231
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
import router from './router'
|
|
281
|
-
import { NovaComponents } from '@nova-design-system/nova-vue/plugin';
|
|
232
|
+
### Option 1: Import in Global CSS (Recommended)
|
|
233
|
+
In your `src/assets/main.css`:
|
|
282
234
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
app.use(router)
|
|
286
|
-
app.use(NovaComponents)
|
|
287
|
-
|
|
288
|
-
app.mount('#app')
|
|
235
|
+
```css
|
|
236
|
+
@import url('contact-us-for-URL/nova-fonts-pro.css');
|
|
289
237
|
```
|
|
290
238
|
|
|
291
239
|
### Option 2: HTML Integration
|
|
@@ -295,7 +243,7 @@ In your `index.html`:
|
|
|
295
243
|
<!DOCTYPE html>
|
|
296
244
|
<html>
|
|
297
245
|
<head>
|
|
298
|
-
<link rel="stylesheet" href="
|
|
246
|
+
<link rel="stylesheet" href="contact-us-for-URL/nova-fonts-pro.css">
|
|
299
247
|
</head>
|
|
300
248
|
<body>
|
|
301
249
|
<div id="app"></div>
|
|
@@ -55,6 +55,8 @@ export declare const NvIconbutton: import("vue").DefineSetupFnComponent<JSX.NvIc
|
|
|
55
55
|
export declare const NvLoader: import("vue").DefineSetupFnComponent<JSX.NvLoader & import("./vue-component-lib/utils").InputProps<string | number | boolean>, {}, {}, JSX.NvLoader & import("./vue-component-lib/utils").InputProps<string | number | boolean> & {}, import("vue").PublicProps>;
|
|
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
|
+
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 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>;
|
|
58
60
|
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>;
|
|
59
61
|
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>;
|
|
60
62
|
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>;
|
|
@@ -99,6 +99,8 @@ export const NvCalendar = /*@__PURE__*/ defineContainer('nv-calendar', undefined
|
|
|
99
99
|
'showActions',
|
|
100
100
|
'shortcuts',
|
|
101
101
|
'showWeekNumbers',
|
|
102
|
+
'cancelLabel',
|
|
103
|
+
'primaryLabel',
|
|
102
104
|
'singleDateChange',
|
|
103
105
|
'rangeDateChange',
|
|
104
106
|
'valueChanged'
|
|
@@ -532,6 +534,21 @@ export const NvMenuitem = /*@__PURE__*/ defineContainer('nv-menuitem', undefined
|
|
|
532
534
|
'name',
|
|
533
535
|
'menuitemSelected'
|
|
534
536
|
]);
|
|
537
|
+
export const NvNotification = /*@__PURE__*/ defineContainer('nv-notification', undefined, [
|
|
538
|
+
'uid',
|
|
539
|
+
'feedback',
|
|
540
|
+
'emphasis',
|
|
541
|
+
'heading',
|
|
542
|
+
'message',
|
|
543
|
+
'icon',
|
|
544
|
+
'dismissible',
|
|
545
|
+
'hidden',
|
|
546
|
+
'initiallyHidden',
|
|
547
|
+
'hiddenChanged'
|
|
548
|
+
], 'hidden', 'hidden-changed');
|
|
549
|
+
export const NvNotificationcontainer = /*@__PURE__*/ defineContainer('nv-notificationcontainer', undefined, [
|
|
550
|
+
'position'
|
|
551
|
+
]);
|
|
535
552
|
export const NvPopover = /*@__PURE__*/ defineContainer('nv-popover', undefined, [
|
|
536
553
|
'triggerElement',
|
|
537
554
|
'open',
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/plugin.d.ts
CHANGED
|
@@ -8,9 +8,19 @@
|
|
|
8
8
|
* By providing custom implementations, we ensure that event names are
|
|
9
9
|
* properly transformed, allowing Stencil components to work seamlessly in Vue.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { App } from 'vue';
|
|
12
|
+
import { type NotificationServiceOptions } from './providers/NotificationService';
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
-
* handlers.
|
|
14
|
+
* Configuration options for the NovaComponents plugin.
|
|
15
15
|
*/
|
|
16
|
-
export
|
|
16
|
+
export interface NovaComponentsOptions {
|
|
17
|
+
/** Notification service configuration options. */
|
|
18
|
+
notifications?: NotificationServiceOptions;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* This is the Vue plugin that is used to define the custom elements, event
|
|
22
|
+
* handlers, and includes the notification service.
|
|
23
|
+
*/
|
|
24
|
+
export declare const NovaComponents: {
|
|
25
|
+
install(app: App, options?: NovaComponentsOptions): Promise<void>;
|
|
26
|
+
};
|
package/dist/plugin.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* properly transformed, allowing Stencil components to work seamlessly in Vue.
|
|
10
10
|
*/
|
|
11
11
|
import { defineCustomElements } from '@nova-design-system/nova-webcomponents/loader';
|
|
12
|
+
import { NvNotificationService, } from './providers/NotificationService';
|
|
12
13
|
/**
|
|
13
14
|
* Transforms the event name to kebab-case.
|
|
14
15
|
* @param {string} eventName - The name of the event.
|
|
@@ -39,15 +40,18 @@ const ael = (el, eventName, cb, opts) => el.addEventListener(transformEventName(
|
|
|
39
40
|
*/
|
|
40
41
|
const rel = (el, eventName, cb, opts) => el.removeEventListener(transformEventName(eventName), cb, opts);
|
|
41
42
|
/**
|
|
42
|
-
* This is the Vue plugin that is used to define the custom elements
|
|
43
|
-
* handlers.
|
|
43
|
+
* This is the Vue plugin that is used to define the custom elements, event
|
|
44
|
+
* handlers, and includes the notification service.
|
|
44
45
|
*/
|
|
45
46
|
export const NovaComponents = {
|
|
46
|
-
async install() {
|
|
47
|
+
async install(app, options = {}) {
|
|
48
|
+
// Define custom elements with event handling
|
|
47
49
|
defineCustomElements(window, {
|
|
48
50
|
ce: (eventName, opts) => new CustomEvent(transformEventName(eventName), opts),
|
|
49
51
|
ael,
|
|
50
52
|
rel,
|
|
51
53
|
});
|
|
54
|
+
// Install notification service
|
|
55
|
+
NvNotificationService.install(app, options.notifications);
|
|
52
56
|
},
|
|
53
57
|
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { App, type Ref, type Component } from 'vue';
|
|
2
|
+
import { NotificationEmphasis, FeedbackColors, NotificationPosition } from '../index';
|
|
3
|
+
/**
|
|
4
|
+
* Action callbacks for notifications. Will render buttons automatically.
|
|
5
|
+
*/
|
|
6
|
+
export interface NotificationAction {
|
|
7
|
+
/** The label of the action. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** The callback to execute when the action is clicked. */
|
|
10
|
+
onClick: () => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Options for creating a notification.
|
|
14
|
+
*/
|
|
15
|
+
export interface NotificationOptions {
|
|
16
|
+
/** Unique identifier for the notification. If not provided, one will be generated. */
|
|
17
|
+
id?: string;
|
|
18
|
+
/** Short and concise text for the notification heading. */
|
|
19
|
+
heading?: string;
|
|
20
|
+
/** Main content of the notification. */
|
|
21
|
+
message?: string;
|
|
22
|
+
/** Whether the notification can be manually dismissed via close button. */
|
|
23
|
+
dismissible?: boolean;
|
|
24
|
+
/** Adjusts the emphasis to make the notification more or less visually
|
|
25
|
+
* prominent to users. Use this to draw attention to important actions or
|
|
26
|
+
* reduce focus on less critical ones */
|
|
27
|
+
emphasis?: `${NotificationEmphasis}`;
|
|
28
|
+
/** Type of the notification, used to determine the color and default icon. */
|
|
29
|
+
feedback?: `${FeedbackColors}`;
|
|
30
|
+
/** Custom icon name to override the default icon. */
|
|
31
|
+
icon?: string;
|
|
32
|
+
/** Notification actions */
|
|
33
|
+
actions?: NotificationAction[];
|
|
34
|
+
/** Custom components for the notification actions. */
|
|
35
|
+
actionSlot?: Component;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A notification with all required fields populated.
|
|
39
|
+
*/
|
|
40
|
+
export interface Notification extends NotificationOptions {
|
|
41
|
+
/** Timestamp when the notification was created. */
|
|
42
|
+
createdAt: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Context value provided by the NotificationService.
|
|
46
|
+
*/
|
|
47
|
+
export interface NotificationContextValue {
|
|
48
|
+
/** Reactive array of active notifications. */
|
|
49
|
+
notifications: Ref<Notification[]>;
|
|
50
|
+
/** Function to show a new notification. Returns the notification ID. */
|
|
51
|
+
show: (options: NotificationOptions) => string;
|
|
52
|
+
/** Function to dismiss a specific notification by ID. This will remove the
|
|
53
|
+
* notification after the animation completes */
|
|
54
|
+
dismiss: (id: string) => void;
|
|
55
|
+
/** Function to immediately remove a specific notification by ID. */
|
|
56
|
+
remove: (id: string) => void;
|
|
57
|
+
/** Function to remove all active notifications immediately. */
|
|
58
|
+
removeAll: () => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Configuration options for the NotificationService plugin.
|
|
62
|
+
*/
|
|
63
|
+
export interface NotificationServiceOptions {
|
|
64
|
+
/** Position of the notification container on the screen. */
|
|
65
|
+
position?: `${NotificationPosition}`;
|
|
66
|
+
/** Maximum number of notifications to display at once. */
|
|
67
|
+
maxNotifications?: number;
|
|
68
|
+
/** Additional CSS class name for the notification container. */
|
|
69
|
+
className?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Vue plugin for Nova notifications.
|
|
73
|
+
*/
|
|
74
|
+
export declare const NvNotificationService: {
|
|
75
|
+
install(app: App, options?: NotificationServiceOptions): void;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Composable to use notifications in any component.
|
|
79
|
+
*
|
|
80
|
+
* @returns {NotificationContextValue} The notification context
|
|
81
|
+
* @throws {Error} If used outside of an app with NvNotificationService installed
|
|
82
|
+
*/
|
|
83
|
+
export declare const useNotifications: () => NotificationContextValue;
|
|
84
|
+
export default NvNotificationService;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/* eslint-disable jsdoc/require-jsdoc */
|
|
2
|
+
import { ref, createApp, h, } from 'vue';
|
|
3
|
+
import { NvButton, NvNotification, NvNotificationcontainer, } from '../generated/components';
|
|
4
|
+
/**
|
|
5
|
+
* Utility function to generate unique IDs.
|
|
6
|
+
*
|
|
7
|
+
* @returns {string} A unique identifier string
|
|
8
|
+
*/
|
|
9
|
+
const generateId = () => {
|
|
10
|
+
return `notification-${Date.now()}-${Math.random()
|
|
11
|
+
.toString(36)
|
|
12
|
+
.substr(2, 9)}`;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Utility function to unwrap a notification element from a ref.
|
|
16
|
+
* @param {NvNotificationRef} el The notification element ref
|
|
17
|
+
* @returns {HTMLNvNotificationElement | null} The notification element
|
|
18
|
+
*/
|
|
19
|
+
const unwrapNotificationEl = (el) => {
|
|
20
|
+
if (!el)
|
|
21
|
+
return null;
|
|
22
|
+
return '$el' in el ? el.$el : el;
|
|
23
|
+
};
|
|
24
|
+
class NotificationManager {
|
|
25
|
+
notifications = ref([]);
|
|
26
|
+
options;
|
|
27
|
+
containerApp = null;
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.options = {
|
|
30
|
+
position: options.position || 'top-right',
|
|
31
|
+
maxNotifications: options.maxNotifications || 50,
|
|
32
|
+
className: options.className || '',
|
|
33
|
+
};
|
|
34
|
+
this.createContainer();
|
|
35
|
+
}
|
|
36
|
+
// keep track of all notification refs by id
|
|
37
|
+
elRefs = new Map();
|
|
38
|
+
// small helper to bind/unbind refs per id
|
|
39
|
+
setElRef = (id) => (el) => {
|
|
40
|
+
if (el)
|
|
41
|
+
this.elRefs.set(id, el);
|
|
42
|
+
else
|
|
43
|
+
this.elRefs.delete(id);
|
|
44
|
+
};
|
|
45
|
+
createContainer() {
|
|
46
|
+
// Create container element
|
|
47
|
+
const containerEl = document.createElement('div');
|
|
48
|
+
containerEl.id = 'nova-notification-container';
|
|
49
|
+
document.body.appendChild(containerEl);
|
|
50
|
+
// Create Vue app for the container
|
|
51
|
+
this.containerApp = createApp({
|
|
52
|
+
setup: () => {
|
|
53
|
+
return () => {
|
|
54
|
+
const items = this.notifications.value.map((notification) => {
|
|
55
|
+
const hasCustomActions = (notification.actions && notification.actions.length > 0) ||
|
|
56
|
+
!!notification.actionSlot;
|
|
57
|
+
const actionChildren = [];
|
|
58
|
+
notification.actions?.forEach((action) => {
|
|
59
|
+
actionChildren.push(h(NvButton, { onClick: action.onClick, emphasis: 'low', size: 'sm' }, { default: () => action.label }));
|
|
60
|
+
});
|
|
61
|
+
if (notification.actionSlot) {
|
|
62
|
+
actionChildren.push(h(notification.actionSlot));
|
|
63
|
+
}
|
|
64
|
+
const actionContainer = hasCustomActions
|
|
65
|
+
? h('div', { slot: 'actions' }, actionChildren)
|
|
66
|
+
: null;
|
|
67
|
+
return h(NvNotification, {
|
|
68
|
+
key: notification.id,
|
|
69
|
+
ref: this.setElRef(notification.id),
|
|
70
|
+
heading: notification.heading,
|
|
71
|
+
message: notification.message,
|
|
72
|
+
dismissible: notification.dismissible,
|
|
73
|
+
emphasis: notification.emphasis,
|
|
74
|
+
feedback: notification.feedback,
|
|
75
|
+
icon: notification.icon,
|
|
76
|
+
initiallyHidden: true,
|
|
77
|
+
onHiddenChanged: (event) => {
|
|
78
|
+
if (event.detail)
|
|
79
|
+
this.handleNotificationClose(notification.id);
|
|
80
|
+
},
|
|
81
|
+
}, {
|
|
82
|
+
default: () => (actionContainer ? [actionContainer] : null),
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return h(NvNotificationcontainer, {
|
|
86
|
+
position: this.options.position,
|
|
87
|
+
class: this.options.className,
|
|
88
|
+
'data-testid': 'notification-container',
|
|
89
|
+
}, {
|
|
90
|
+
// ✅ function slot avoids the warning
|
|
91
|
+
default: () => items,
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
this.containerApp.mount(containerEl);
|
|
97
|
+
}
|
|
98
|
+
handleNotificationClose(id) {
|
|
99
|
+
this.remove(id);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Show a new notification.
|
|
103
|
+
*
|
|
104
|
+
* @param {NotificationOptions} options - The notification options
|
|
105
|
+
* @returns {string} The notification ID
|
|
106
|
+
*/
|
|
107
|
+
show = (options) => {
|
|
108
|
+
const id = options.id || generateId();
|
|
109
|
+
const notification = {
|
|
110
|
+
id,
|
|
111
|
+
heading: options.heading,
|
|
112
|
+
message: options.message,
|
|
113
|
+
dismissible: options.dismissible ?? true,
|
|
114
|
+
emphasis: options.emphasis ?? 'medium',
|
|
115
|
+
feedback: options.feedback ?? 'information',
|
|
116
|
+
actions: options.actions ?? [],
|
|
117
|
+
actionSlot: options.actionSlot,
|
|
118
|
+
icon: options.icon,
|
|
119
|
+
createdAt: Date.now(),
|
|
120
|
+
};
|
|
121
|
+
// Remove oldest notifications if we exceed max
|
|
122
|
+
const newNotifications = [notification, ...this.notifications.value];
|
|
123
|
+
if (newNotifications.length > this.options.maxNotifications) {
|
|
124
|
+
this.notifications.value = newNotifications.slice(0, this.options.maxNotifications);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.notifications.value = newNotifications;
|
|
128
|
+
}
|
|
129
|
+
setTimeout(() => {
|
|
130
|
+
const ref = this.elRefs.get(id);
|
|
131
|
+
const el = unwrapNotificationEl(ref);
|
|
132
|
+
el?.show();
|
|
133
|
+
}, 0);
|
|
134
|
+
return id;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Dismiss a specific notification by ID. This will remove the notification
|
|
138
|
+
* after the animation completes.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} id - The notification ID to dismiss
|
|
141
|
+
*/
|
|
142
|
+
dismiss = (id) => {
|
|
143
|
+
const ref = this.elRefs.get(id);
|
|
144
|
+
const el = unwrapNotificationEl(ref);
|
|
145
|
+
el?.dismiss();
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Immediately remove a specific notification by ID.
|
|
149
|
+
*
|
|
150
|
+
* @param {string} id - The notification ID to dismiss
|
|
151
|
+
*/
|
|
152
|
+
remove = (id) => {
|
|
153
|
+
this.notifications.value = this.notifications.value.filter((notification) => notification.id !== id);
|
|
154
|
+
this.elRefs.delete(id);
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Clear all active notifications.
|
|
158
|
+
*/
|
|
159
|
+
removeAll = () => {
|
|
160
|
+
this.notifications.value = [];
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Get the current context value.
|
|
164
|
+
*
|
|
165
|
+
* @returns {NotificationContextValue} The context value
|
|
166
|
+
*/
|
|
167
|
+
getContextValue = () => ({
|
|
168
|
+
notifications: this.notifications,
|
|
169
|
+
show: this.show,
|
|
170
|
+
remove: this.remove,
|
|
171
|
+
dismiss: this.dismiss,
|
|
172
|
+
removeAll: this.removeAll,
|
|
173
|
+
});
|
|
174
|
+
/**
|
|
175
|
+
* Destroy the notification manager and clean up resources.
|
|
176
|
+
*/
|
|
177
|
+
destroy() {
|
|
178
|
+
if (this.containerApp) {
|
|
179
|
+
this.containerApp.unmount();
|
|
180
|
+
this.containerApp = null;
|
|
181
|
+
}
|
|
182
|
+
const containerEl = document.getElementById('nova-notification-container');
|
|
183
|
+
if (containerEl) {
|
|
184
|
+
document.body.removeChild(containerEl);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Global notification manager instance
|
|
189
|
+
let notificationManager = null;
|
|
190
|
+
/**
|
|
191
|
+
* Vue plugin for Nova notifications.
|
|
192
|
+
*/
|
|
193
|
+
export const NvNotificationService = {
|
|
194
|
+
install(app, options = {}) {
|
|
195
|
+
notificationManager = new NotificationManager(options);
|
|
196
|
+
// Provide global property
|
|
197
|
+
app.config.globalProperties.$notifications =
|
|
198
|
+
notificationManager.getContextValue();
|
|
199
|
+
// Provide for composition API
|
|
200
|
+
app.provide('notifications', notificationManager.getContextValue());
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Composable to use notifications in any component.
|
|
205
|
+
*
|
|
206
|
+
* @returns {NotificationContextValue} The notification context
|
|
207
|
+
* @throws {Error} If used outside of an app with NvNotificationService installed
|
|
208
|
+
*/
|
|
209
|
+
export const useNotifications = () => {
|
|
210
|
+
if (!notificationManager) {
|
|
211
|
+
throw new Error('useNotifications must be used in an app with NovaComponents installed. Use app.use(NovaComponents) in your main.ts');
|
|
212
|
+
}
|
|
213
|
+
return notificationManager.getContextValue();
|
|
214
|
+
};
|
|
215
|
+
export default NvNotificationService;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { NvNotificationService, useNotifications, default as NotificationService, } from './NotificationService';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nova-design-system/nova-vue",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0-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",
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "npm run tsc",
|
|
34
|
+
"build:watch": "npm run tsc:watch",
|
|
34
35
|
"tsc": "tsc -p . --outDir ./dist",
|
|
36
|
+
"tsc:watch": "tsc -p . --outDir ./dist --watch",
|
|
35
37
|
"storybook": "storybook dev -p 6008",
|
|
36
38
|
"storybook.build": "storybook build -o ../../storybook-static/vue",
|
|
37
39
|
"clean": "rimraf dist lib/generated",
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
"@nova-design-system/nova-webcomponents": "*"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
|
-
"vue": "3.
|
|
46
|
+
"vue": "3.5.17",
|
|
45
47
|
"nova-utils": "*",
|
|
46
48
|
"nova-storybook-utils": "*"
|
|
47
49
|
}
|