@nexdom/uimed-vue 1.2.0-beta.1 → 1.2.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +145 -0
- package/CLAUDE.md +1 -0
- package/README.md +13 -0
- package/dist/components.d.ts +157 -3
- package/dist/components.js +499 -153
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/unit-test.js +1 -1
- package/package.json +7 -2
package/AGENTS.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Project
|
|
4
|
+
|
|
5
|
+
`@nexdom/uimed-vue` is a Vue 3 UI component library for NEXDOM applications, built on top of [Vuetify](https://vuetifyjs.com/) and [Material Design 3](https://m3.material.io/). It's published to npm and documented via VitePress at https://nexdom-healthtech.github.io/uimed-vue/.
|
|
6
|
+
|
|
7
|
+
Tooling is built around [Vite+](https://github.com/voidzero-dev/vite-plus) (`vp`/`vpr`/`vpx` CLIs), not raw `vite`/`vitest`/`eslint` invocations.
|
|
8
|
+
|
|
9
|
+
## Using this library in an app
|
|
10
|
+
|
|
11
|
+
These steps are for developers _consuming_ `@nexdom/uimed-vue` in a Vue application (not for contributing to this repo — see the rest of this document for that).
|
|
12
|
+
|
|
13
|
+
### Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
vp add @nexdom/uimed-vue
|
|
17
|
+
# But, if you're not using Vite+ yet...
|
|
18
|
+
npm i @nexdom/uimed-vue
|
|
19
|
+
# Or
|
|
20
|
+
pnpm add @nexdom/uimed-vue
|
|
21
|
+
# Or
|
|
22
|
+
yarn add @nexdom/uimed-vue
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Peer dependencies (`@fontsource/roboto`, `@mdi/font`, `@nexdom/shared`, `resize-observer-polyfill`, `vite-plugin-vuetify`, `vite-plus`, `vue`, `vue-router`, `vuetify`) must be installed alongside it — most package managers install these automatically, but confirm versions match what's declared in `package.json`'s `peerDependencies`.
|
|
26
|
+
|
|
27
|
+
### Setup
|
|
28
|
+
|
|
29
|
+
Add the Vite plugin:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
// vite.config.ts
|
|
33
|
+
import { vitePluginUimed } from "@nexdom/uimed-vue/plugins.ts";
|
|
34
|
+
|
|
35
|
+
// ...
|
|
36
|
+
|
|
37
|
+
plugins: [vue(), vitePluginUimed()];
|
|
38
|
+
|
|
39
|
+
// ...
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Register the plugin in the Vue app:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// main.js or main.ts
|
|
46
|
+
import { createApp } from "vue";
|
|
47
|
+
import { createUimed } from "@nexdom/uimed-vue";
|
|
48
|
+
|
|
49
|
+
import App from "./App.vue";
|
|
50
|
+
|
|
51
|
+
const uimed = createUimed();
|
|
52
|
+
|
|
53
|
+
createApp(App).use(uimed).mount("#app");
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Usage
|
|
57
|
+
|
|
58
|
+
Place the `Root` component at the top of `App.vue`, then add other components as needed:
|
|
59
|
+
|
|
60
|
+
```vue
|
|
61
|
+
<!-- App.vue -->
|
|
62
|
+
<template>
|
|
63
|
+
<root>
|
|
64
|
+
<!-- ... -->
|
|
65
|
+
</root>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
import { Root } from "@nexdom/uimed-vue/components";
|
|
70
|
+
</script>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Never write CSS, classes or any kind of styling. Always use component props.
|
|
74
|
+
|
|
75
|
+
Available entry points: `@nexdom/uimed-vue` (root), `@nexdom/uimed-vue/components`, `@nexdom/uimed-vue/composables`, `@nexdom/uimed-vue/plugins`, `@nexdom/uimed-vue/unit-test` (test helpers, e.g. `vueTestUtilsPluginUimed()` for mounting components with Vuetify in Vitest).
|
|
76
|
+
|
|
77
|
+
Full component/composable reference lives in the [docs](https://nexdom-healthtech.github.io/uimed-vue/).
|
|
78
|
+
|
|
79
|
+
## Contributing to this repo
|
|
80
|
+
|
|
81
|
+
The sections below are for developers working _on_ this library itself.
|
|
82
|
+
|
|
83
|
+
### Dev environment setup
|
|
84
|
+
|
|
85
|
+
This project expects to be opened inside its devcontainer (VSCode). If commands below fail with missing shims/commands, run:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
vp env doctor # diagnose missing parts
|
|
89
|
+
vp env setup # create shims like vpr and vpx
|
|
90
|
+
vp install # install dependencies
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Commands
|
|
94
|
+
|
|
95
|
+
- `vpr check` — lint, formatter, and type-check (requires `build`/`pack` to have run first for the type-check step)
|
|
96
|
+
- `vp test --coverage` — unit tests with coverage (Vitest, jsdom, 100% coverage threshold enforced)
|
|
97
|
+
- `vpr test:mutations` — mutation tests (Stryker; thresholds: high 100, low 95, break 95)
|
|
98
|
+
- `vpr test:e2e` — E2E tests (Playwright, runs against the built docs preview site)
|
|
99
|
+
- `vpr depcruise` — architecture/dependency rules (dependency-cruiser)
|
|
100
|
+
- `vp pack` / `vpr build` — build the library
|
|
101
|
+
- `vpr docs` / `vpr docs:dev` — run docs site (imports the lib from `dist`, not `src` — run `vpr dev` in a second terminal to keep `dist` updated while iterating)
|
|
102
|
+
|
|
103
|
+
CI (`.github/workflows/ci.yml`) runs, in order: commitlint on PR commits, `vp pack`, `vpr check`, `vpr depcruise`, `vp test --coverage`, `vpr test:mutations`, `vpr test:e2e`. Match this locally before opening a PR.
|
|
104
|
+
|
|
105
|
+
## Code conventions
|
|
106
|
+
|
|
107
|
+
- All `src` code is written in English. `docs` content is written in Portuguese (aimed at Brazilian users), even though file/dir names stay in English.
|
|
108
|
+
- Never write CSS, classes or any kind of styling. Always use component props.
|
|
109
|
+
- Path aliases: `@/*` → `src/*`, `@e2e/*` → `e2e/*`.
|
|
110
|
+
- Every public component follows this pattern to block access to internals and give it an editor-hover description:
|
|
111
|
+
|
|
112
|
+
```vue
|
|
113
|
+
<script lang="ts">
|
|
114
|
+
/**
|
|
115
|
+
* A short description of the component, shown when hovering it in the
|
|
116
|
+
* editor.
|
|
117
|
+
*/
|
|
118
|
+
export default {
|
|
119
|
+
inheritAttrs: false,
|
|
120
|
+
};
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<script setup lang="ts">
|
|
124
|
+
// component's implementation
|
|
125
|
+
</script>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- Component layout: `src/components/<name>/<name>.vue`, `types.ts` for prop/option types, `__tests__/<name>.test.ts` for unit tests. Composables follow the same shape under `src/composables/<name>/`.
|
|
129
|
+
- Public components/composables/types are re-exported from `src/components/index.ts` and `src/composables/index.ts`.
|
|
130
|
+
- Use [JSDoc](https://jsdoc.app/about-getting-started) on every method/prop/type intended to be part of the public API — it's the primary documentation surface and supports markdown/code examples.
|
|
131
|
+
- Known workarounds (see CONTRIBUTING.md before touching related config): `stryker-vue-ignorer` patches a Stryker/Vue macro-hoisting issue; `vue-tsc` is used for type-check instead of Vite+'s built-in one due to an oxlint/Vue support gap. Both are meant to be removed once their upstream issues are fixed — don't build further on top of them without checking if they're still needed.
|
|
132
|
+
|
|
133
|
+
## Testing conventions
|
|
134
|
+
|
|
135
|
+
- Unit tests use Vitest + `@vue/test-utils`, with `vueTestUtilsPluginUimed()` from `@/unit-test.ts` to mount a Vuetify instance.
|
|
136
|
+
- Global unit test setup (`src/__tests__/setup.ts`) stubs `visualViewport`, uses fake timers, and silences `console.error/warn/log`.
|
|
137
|
+
- Coverage threshold is 100%; mutation testing threshold is 100% (break at 100). Don't add code paths without covering tests.
|
|
138
|
+
- E2E tests (Playwright, `e2e/`) run against the built docs preview (`http://localhost:4173/uimed-vue/`). Snapshots/screenshots live under `__snapshots__`/`__screenshot__` next to each spec.
|
|
139
|
+
|
|
140
|
+
## Git workflow
|
|
141
|
+
|
|
142
|
+
- Trunk-based development. `main` is the only long-lived branch (`beta`/`alpha` exist only for pre-release/prototype work — see CONTRIBUTING.md for when to use them). `main`, `beta`, `alpha` are all protected against direct pushes.
|
|
143
|
+
- Short-lived branches are named after their intention (e.g. `fix-some-method-behavior`) and are deleted after merging back via PR.
|
|
144
|
+
- Commit messages MUST follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) — enforced by commitlint in CI. Recent examples: `feat(components): ...`, `fix(composables): ...`, `test(e2e): ...`, `docs(root): ...`, `chore: ...`.
|
|
145
|
+
- Releases are automated via `semantic-release` (conventional-commits preset) — don't hand-edit versions/changelogs.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
package/README.md
CHANGED
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
[](https://github.com/nexdom-healthtech/uimed-vue/actions/workflows/cd.yml)
|
|
5
5
|
[](https://github.com/nexdom-healthtech/uimed-vue/actions/workflows/dependabot/dependabot-updates)
|
|
6
6
|
|
|
7
|
+
[](https://sonar-qube-public.services-dev.nexdom.tec.br/dashboard?id=UIMed-Vue)
|
|
8
|
+
[](https://sonar-qube-public.services-dev.nexdom.tec.br/dashboard?id=UIMed-Vue)
|
|
9
|
+
[](https://sonar-qube-public.services-dev.nexdom.tec.br/dashboard?id=UIMed-Vue)
|
|
10
|
+
[](https://sonar-qube-public.services-dev.nexdom.tec.br/dashboard?id=UIMed-Vue)
|
|
11
|
+
[](https://sonar-qube-public.services-dev.nexdom.tec.br/dashboard?id=UIMed-Vue)
|
|
12
|
+
[](https://sonar-qube-public.services-dev.nexdom.tec.br/dashboard?id=UIMed-Vue)
|
|
13
|
+
[](https://sonar-qube-public.services-dev.nexdom.tec.br/dashboard?id=UIMed-Vue)
|
|
14
|
+
[](https://sonar-qube-public.services-dev.nexdom.tec.br/dashboard?id=UIMed-Vue)
|
|
15
|
+
|
|
16
|
+
[](https://nexdom-healthtech.github.io/uimed-vue)
|
|
17
|
+
|
|
18
|
+
[](https://github.com/semantic-release/semantic-release)
|
|
19
|
+
|
|
7
20
|
A ui framework for NEXDOM applications, based on [Material Design](https://m3.material.io/).
|
|
8
21
|
|
|
9
22
|
For more examples and information, check the [docs page](https://nexdom-healthtech.github.io/uimed-vue/).
|
package/dist/components.d.ts
CHANGED
|
@@ -1,4 +1,138 @@
|
|
|
1
1
|
import { t as ColorVariant } from "./types-BPXXtbmW.js";
|
|
2
|
+
import { RouteLocationRaw } from "vue-router";
|
|
3
|
+
//#region src/components/app-bar/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Props exposed by the {@link AppBar} component.
|
|
6
|
+
*/
|
|
7
|
+
type AppBarProps = {
|
|
8
|
+
/**
|
|
9
|
+
* Component id to use on automated tests.
|
|
10
|
+
*/
|
|
11
|
+
dataTestid?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Main title to be presented.
|
|
14
|
+
*/
|
|
15
|
+
title?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Help page route.
|
|
18
|
+
*/
|
|
19
|
+
help?: RouteLocationRaw;
|
|
20
|
+
/**
|
|
21
|
+
* Resources available to the user in the app-bar
|
|
22
|
+
*/
|
|
23
|
+
user?: AppBarUserProps;
|
|
24
|
+
/**
|
|
25
|
+
* List of notifications to show in the {@link AppBar}.
|
|
26
|
+
*/
|
|
27
|
+
notifications?: Array<AppBarNotification>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* A single notification entry shown in the {@link AppBar}.
|
|
31
|
+
*/
|
|
32
|
+
interface AppBarNotification {
|
|
33
|
+
/**
|
|
34
|
+
* Main text describing the notification.
|
|
35
|
+
*/
|
|
36
|
+
title: string;
|
|
37
|
+
/**
|
|
38
|
+
* Complementary text with additional details.
|
|
39
|
+
*/
|
|
40
|
+
subtitle?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Whether the notification has already been read.
|
|
43
|
+
*/
|
|
44
|
+
read?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Date the notification was generated.
|
|
47
|
+
*/
|
|
48
|
+
date?: Date;
|
|
49
|
+
}
|
|
50
|
+
interface AppBarUserProps {
|
|
51
|
+
/**
|
|
52
|
+
* Major identifier, like the user full name.
|
|
53
|
+
*/
|
|
54
|
+
title?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Complement to user identifier, like it's e-mail or username.
|
|
57
|
+
*/
|
|
58
|
+
subtitle?: string;
|
|
59
|
+
/**
|
|
60
|
+
* URL leading to a user picture.
|
|
61
|
+
*/
|
|
62
|
+
img?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Options to show the user when its menu is opened.
|
|
65
|
+
* Can be plain options, grouped or both.
|
|
66
|
+
*/
|
|
67
|
+
options?: Array<ParentOptionOrOption>;
|
|
68
|
+
}
|
|
69
|
+
type ParentOptionOrOption = ParentOption | Option;
|
|
70
|
+
type ParentOption = {
|
|
71
|
+
/**
|
|
72
|
+
* Text to present for the group.
|
|
73
|
+
*/
|
|
74
|
+
description: string;
|
|
75
|
+
/**
|
|
76
|
+
* Child {@link Option} list.
|
|
77
|
+
*/
|
|
78
|
+
items: Array<Option>;
|
|
79
|
+
};
|
|
80
|
+
interface Option {
|
|
81
|
+
/**
|
|
82
|
+
* Text to present for the action.
|
|
83
|
+
*/
|
|
84
|
+
description: string;
|
|
85
|
+
/**
|
|
86
|
+
* Route to navigate when item is clicked.
|
|
87
|
+
*/
|
|
88
|
+
route?: RouteLocationRaw;
|
|
89
|
+
/**
|
|
90
|
+
* Callback to be triggered when the item is clicked.
|
|
91
|
+
*/
|
|
92
|
+
action?: () => void;
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/components/navigation-menu/types.d.ts
|
|
96
|
+
/**
|
|
97
|
+
* Props exposed by the {@link NavigationMenu} component.
|
|
98
|
+
*/
|
|
99
|
+
type NavigationMenuProps = {
|
|
100
|
+
/**
|
|
101
|
+
* Component id to use on automated tests.
|
|
102
|
+
*/
|
|
103
|
+
dataTestid?: string;
|
|
104
|
+
/**
|
|
105
|
+
* List of items to show in the {@link NavigationMenu}.
|
|
106
|
+
* Can be plain items, grouped or both.
|
|
107
|
+
*/
|
|
108
|
+
items?: Array<NavigationMenuParentItemOrItem>;
|
|
109
|
+
};
|
|
110
|
+
type NavigationMenuParentItemOrItem = NavigationMenuParentItem | NavigationMenuItem;
|
|
111
|
+
type NavigationMenuParentItem = {
|
|
112
|
+
/**
|
|
113
|
+
* Text to present for the group.
|
|
114
|
+
*/
|
|
115
|
+
description: string;
|
|
116
|
+
/**
|
|
117
|
+
* Child {@link NavigationMenuItem} list.
|
|
118
|
+
*/
|
|
119
|
+
items: Array<NavigationMenuItem>;
|
|
120
|
+
};
|
|
121
|
+
interface NavigationMenuItem {
|
|
122
|
+
/**
|
|
123
|
+
* Text to present for the item.
|
|
124
|
+
*/
|
|
125
|
+
description: string;
|
|
126
|
+
/**
|
|
127
|
+
* Route to navigate when item is clicked.
|
|
128
|
+
*/
|
|
129
|
+
route?: RouteLocationRaw;
|
|
130
|
+
/**
|
|
131
|
+
* Callback to be triggered when the item is clicked.
|
|
132
|
+
*/
|
|
133
|
+
action?: () => void;
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
2
136
|
//#region src/components/root/types.d.ts
|
|
3
137
|
/**
|
|
4
138
|
* Props exposed by the {@link Root} component.
|
|
@@ -8,6 +142,14 @@ type RootProps = {
|
|
|
8
142
|
* Component id to use on automated tests.
|
|
9
143
|
*/
|
|
10
144
|
dataTestid?: string;
|
|
145
|
+
/**
|
|
146
|
+
* App bar properties.
|
|
147
|
+
*/
|
|
148
|
+
appBar?: AppBarProps;
|
|
149
|
+
/**
|
|
150
|
+
* Navigation menu properties.
|
|
151
|
+
*/
|
|
152
|
+
navigationMenu?: NavigationMenuProps;
|
|
11
153
|
};
|
|
12
154
|
//#endregion
|
|
13
155
|
//#region src/components/root/root.vue.d.ts
|
|
@@ -24,7 +166,15 @@ type RootProps = {
|
|
|
24
166
|
* @see {@link https://nexdom-healthtech.github.io/uimed-vue/guide/components/root | Root Guide}
|
|
25
167
|
*/
|
|
26
168
|
declare const _default$4: typeof __VLS_export$6;
|
|
27
|
-
declare const __VLS_export$6: __VLS_WithSlots$5<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<RootProps
|
|
169
|
+
declare const __VLS_export$6: __VLS_WithSlots$5<import("vue", { with: { "resolution-mode": "import" } }).DefineComponent<RootProps & {
|
|
170
|
+
notificationsOpen?: boolean;
|
|
171
|
+
}, {}, {}, {}, {}, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, import("vue", { with: { "resolution-mode": "import" } }).ComponentOptionsMixin, {
|
|
172
|
+
"update:notificationsOpen": (value: boolean) => any;
|
|
173
|
+
}, string, import("vue", { with: { "resolution-mode": "import" } }).PublicProps, Readonly<RootProps & {
|
|
174
|
+
notificationsOpen?: boolean;
|
|
175
|
+
}> & Readonly<{
|
|
176
|
+
"onUpdate:notificationsOpen"?: ((value: boolean) => any) | undefined;
|
|
177
|
+
}>, {}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>, {
|
|
28
178
|
default?: (props: {}) => any;
|
|
29
179
|
}>;
|
|
30
180
|
type __VLS_WithSlots$5<T, S> = T & {
|
|
@@ -188,7 +338,11 @@ declare const __VLS_export$4: import("vue", { with: { "resolution-mode": "import
|
|
|
188
338
|
modelValue?: string;
|
|
189
339
|
}> & Readonly<{
|
|
190
340
|
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
191
|
-
}>, {
|
|
341
|
+
}>, {
|
|
342
|
+
type: TextFieldType;
|
|
343
|
+
variant: TextFieldVariant;
|
|
344
|
+
clearable: boolean;
|
|
345
|
+
}, {}, {}, {}, string, import("vue", { with: { "resolution-mode": "import" } }).ComponentProvideOptions, false, {}, any>;
|
|
192
346
|
//#endregion
|
|
193
347
|
//#region src/components/grid/container/types.d.ts
|
|
194
348
|
/**
|
|
@@ -352,4 +506,4 @@ type __VLS_WithSlots<T, S> = T & {
|
|
|
352
506
|
};
|
|
353
507
|
};
|
|
354
508
|
//#endregion
|
|
355
|
-
export { _default as Btn, _default$1 as Column, _default$2 as Container, _default$3 as Frm, _default$4 as Root, _default$5 as Row, _default$6 as TextField };
|
|
509
|
+
export { type AppBarProps as AppBarConfig, type AppBarUserProps as AppBarUserConfig, _default as Btn, _default$1 as Column, _default$2 as Container, _default$3 as Frm, type NavigationMenuProps as NavigationMenuConfig, _default$4 as Root, _default$5 as Row, _default$6 as TextField };
|
package/dist/components.js
CHANGED
|
@@ -1,171 +1,216 @@
|
|
|
1
1
|
import { r as useVuetifyColor, t as messages } from "./use-toast-CACuYhK_.js";
|
|
2
|
-
import { computed, createBlock,
|
|
3
|
-
import { VApp, VBtn, VCol, VContainer, VForm, VIconBtn, VMain, VRow, VSnackbarQueue, VTextField } from "vuetify/components";
|
|
4
|
-
import { isEmail, isEmpty, isPhone, isUrl } from "@nexdom/shared/utils";
|
|
5
|
-
//#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
inheritAttrs: false,
|
|
9
|
-
__name: "container",
|
|
10
|
-
props: { dataTestid: {
|
|
11
|
-
type: String,
|
|
12
|
-
required: false
|
|
13
|
-
} },
|
|
14
|
-
setup(__props) {
|
|
15
|
-
const props = __props;
|
|
16
|
-
return (_ctx, _cache) => {
|
|
17
|
-
return openBlock(), createBlock(unref(VContainer), {
|
|
18
|
-
"data-testid": props.dataTestid,
|
|
19
|
-
fluid: ""
|
|
20
|
-
}, {
|
|
21
|
-
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
22
|
-
_: 3
|
|
23
|
-
}, 8, ["data-testid"]);
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region src/components/dialogs/toast.vue
|
|
29
|
-
var toast_default = /* @__PURE__ */ defineComponent({
|
|
30
|
-
inheritAttrs: false,
|
|
31
|
-
__name: "toast",
|
|
32
|
-
setup(__props) {
|
|
33
|
-
return (_ctx, _cache) => {
|
|
34
|
-
return openBlock(), createBlock(unref(VSnackbarQueue), {
|
|
35
|
-
modelValue: unref(messages),
|
|
36
|
-
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(messages) ? messages.value = $event : null),
|
|
37
|
-
"display-strategy": "overflow",
|
|
38
|
-
location: "top end",
|
|
39
|
-
"total-visible": "3",
|
|
40
|
-
collapsed: "",
|
|
41
|
-
closable: "",
|
|
42
|
-
timer: ""
|
|
43
|
-
}, {
|
|
44
|
-
actions: withCtx(({ props }) => [createVNode(unref(VIconBtn), mergeProps(props, {
|
|
45
|
-
"aria-label": "Fechar",
|
|
46
|
-
icon: "$close",
|
|
47
|
-
size: "small",
|
|
48
|
-
variant: "text"
|
|
49
|
-
}), null, 16)]),
|
|
50
|
-
_: 1
|
|
51
|
-
}, 8, ["modelValue"]);
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
//#endregion
|
|
56
|
-
//#region src/components/root/root.vue
|
|
57
|
-
var root_default = /* @__PURE__ */ defineComponent({
|
|
58
|
-
inheritAttrs: false,
|
|
59
|
-
__name: "root",
|
|
60
|
-
props: { dataTestid: {
|
|
61
|
-
type: String,
|
|
62
|
-
required: false
|
|
63
|
-
} },
|
|
64
|
-
setup(__props) {
|
|
65
|
-
const props = __props;
|
|
66
|
-
return (_ctx, _cache) => {
|
|
67
|
-
return openBlock(), createBlock(unref(VApp), { "data-testid": props.dataTestid }, {
|
|
68
|
-
default: withCtx(() => [createVNode(unref(VMain), null, {
|
|
69
|
-
default: withCtx(() => [createVNode(container_default, null, {
|
|
70
|
-
default: withCtx(() => [renderSlot(_ctx.$slots, "default"), createVNode(toast_default)]),
|
|
71
|
-
_: 3
|
|
72
|
-
})]),
|
|
73
|
-
_: 3
|
|
74
|
-
})]),
|
|
75
|
-
_: 3
|
|
76
|
-
}, 8, ["data-testid"]);
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
//#endregion
|
|
81
|
-
//#region src/composables/btn.ts
|
|
82
|
-
const btnVariantToVuetifyVariant = {
|
|
83
|
-
primary: "elevated",
|
|
84
|
-
secondary: "flat",
|
|
85
|
-
ghost: "outlined"
|
|
86
|
-
};
|
|
87
|
-
function useBtnVariant(variant) {
|
|
88
|
-
return computed(() => {
|
|
89
|
-
const variantValue = toValue(variant);
|
|
90
|
-
return variantValue ? btnVariantToVuetifyVariant[variantValue] : btnVariantToVuetifyVariant.primary;
|
|
91
|
-
});
|
|
2
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, isRef, mergeModels, mergeProps, openBlock, ref, renderList, renderSlot, toDisplayString, toValue, unref, useModel, withCtx, withModifiers } from "vue";
|
|
3
|
+
import { VApp, VAppBar, VAppBarNavIcon, VAvatar, VBadge, VBtn, VCard, VCardText, VCol, VContainer, VDivider, VForm, VIcon, VIconBtn, VList, VListGroup, VListItem, VListItemAction, VListSubheader, VMain, VMenu, VNavigationDrawer, VRow, VSnackbarQueue, VTextField } from "vuetify/components";
|
|
4
|
+
import { formatDateTime, isEmail, isEmpty, isPhone, isUrl, toPeriodInterval } from "@nexdom/shared/utils";
|
|
5
|
+
//#region src/composables/navigation/use-route-props.ts
|
|
6
|
+
function useRouteProps(route) {
|
|
7
|
+
return computed(() => routeToProps(route));
|
|
92
8
|
}
|
|
93
|
-
function
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
9
|
+
function routeToProps(route) {
|
|
10
|
+
const location = toValue(route);
|
|
11
|
+
return isExternalRoute(location) ? { href: location } : { to: location };
|
|
97
12
|
}
|
|
98
|
-
function
|
|
99
|
-
return
|
|
13
|
+
function isExternalRoute(route) {
|
|
14
|
+
return typeof route === "string" && route.startsWith("http");
|
|
100
15
|
}
|
|
101
16
|
//#endregion
|
|
102
|
-
//#region src/components/
|
|
103
|
-
|
|
17
|
+
//#region src/components/app-bar/app-bar.vue?vue&type=script&setup=true&lang.ts
|
|
18
|
+
const _hoisted_1$1 = { class: "opacity-60" };
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/components/app-bar/app-bar.vue
|
|
21
|
+
var app_bar_default = /* @__PURE__ */ defineComponent({
|
|
104
22
|
inheritAttrs: false,
|
|
105
|
-
__name: "
|
|
106
|
-
props: {
|
|
107
|
-
|
|
23
|
+
__name: "app-bar",
|
|
24
|
+
props: /*@__PURE__*/ mergeModels({
|
|
25
|
+
dataTestid: {
|
|
108
26
|
type: String,
|
|
109
27
|
required: false
|
|
110
28
|
},
|
|
111
|
-
|
|
29
|
+
title: {
|
|
112
30
|
type: String,
|
|
113
31
|
required: false
|
|
114
32
|
},
|
|
115
|
-
|
|
116
|
-
type:
|
|
33
|
+
help: {
|
|
34
|
+
type: null,
|
|
117
35
|
required: false
|
|
118
36
|
},
|
|
119
|
-
|
|
120
|
-
type:
|
|
37
|
+
user: {
|
|
38
|
+
type: Object,
|
|
121
39
|
required: false
|
|
122
40
|
},
|
|
123
|
-
|
|
124
|
-
type:
|
|
125
|
-
required: false
|
|
126
|
-
},
|
|
127
|
-
form: {
|
|
128
|
-
type: String,
|
|
41
|
+
notifications: {
|
|
42
|
+
type: Array,
|
|
129
43
|
required: false
|
|
130
44
|
},
|
|
131
|
-
|
|
132
|
-
type:
|
|
133
|
-
required:
|
|
45
|
+
navigation: {
|
|
46
|
+
type: Boolean,
|
|
47
|
+
required: true
|
|
134
48
|
}
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
|
|
49
|
+
}, {
|
|
50
|
+
"notificationsOpen": {
|
|
51
|
+
type: Boolean,
|
|
52
|
+
default: false
|
|
53
|
+
},
|
|
54
|
+
"notificationsOpenModifiers": {},
|
|
55
|
+
"navigationOpen": { type: Boolean },
|
|
56
|
+
"navigationOpenModifiers": {}
|
|
57
|
+
}),
|
|
58
|
+
emits: ["update:notificationsOpen", "update:navigationOpen"],
|
|
59
|
+
setup(__props) {
|
|
138
60
|
const props = __props;
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
61
|
+
const notificationsOpen = useModel(__props, "notificationsOpen");
|
|
62
|
+
const navigationOpen = useModel(__props, "navigationOpen");
|
|
63
|
+
const helpBtnDataTestid = computed(() => `${props.dataTestid}-help`);
|
|
64
|
+
const notificationsBtnDataTestid = computed(() => `${props.dataTestid}-notifications`);
|
|
65
|
+
const navigationBtnDataTestid = computed(() => `${props.dataTestid}-navigation`);
|
|
66
|
+
const notificationsMenuDataTestid = computed(() => `${props.dataTestid}-notifications-menu`);
|
|
67
|
+
const userBtnDataTestid = computed(() => `${props.dataTestid}-user`);
|
|
68
|
+
const userMenuDataTestid = computed(() => `${props.dataTestid}-user-menu`);
|
|
69
|
+
const showHelp = computed(() => !!props.help);
|
|
70
|
+
const routeProps = useRouteProps(() => props.help);
|
|
71
|
+
const showNotifications = computed(() => !!props.notifications);
|
|
72
|
+
const unreadCount = computed(() => props.notifications.filter((notification) => !notification.read).length);
|
|
73
|
+
const hasUnreadNotifications = computed(() => unreadCount.value > 0);
|
|
74
|
+
const showUser = computed(() => !!props.user);
|
|
75
|
+
const userAvatarProps = computed(() => props.user.img ? { image: props.user.img } : { icon: "mdi-account-outline" });
|
|
76
|
+
function isParentOption(option) {
|
|
77
|
+
return "items" in option;
|
|
78
|
+
}
|
|
79
|
+
function formatNotificationDate(date) {
|
|
80
|
+
const interval = toPeriodInterval(date);
|
|
81
|
+
if (wasThisMonth(interval)) {
|
|
82
|
+
if (wasToday(interval)) return formatDateTime(date, "HH:mm");
|
|
83
|
+
else if (wasYesterday(interval)) return "Ontem";
|
|
84
|
+
}
|
|
85
|
+
return formatDateTime(date, "DD/MM/YYYY");
|
|
86
|
+
}
|
|
87
|
+
function wasThisMonth(interval) {
|
|
88
|
+
return interval.months === 0 && interval.years === 0;
|
|
89
|
+
}
|
|
90
|
+
function wasToday(interval) {
|
|
91
|
+
return interval.days === 0;
|
|
92
|
+
}
|
|
93
|
+
function wasYesterday(interval) {
|
|
94
|
+
return interval.days === 1;
|
|
146
95
|
}
|
|
147
96
|
return (_ctx, _cache) => {
|
|
148
|
-
return openBlock(), createBlock(unref(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"data-testid": props.dataTestid,
|
|
152
|
-
variant: unref(variant),
|
|
153
|
-
color: unref(color),
|
|
154
|
-
type: unref(type),
|
|
155
|
-
form: unref(form),
|
|
156
|
-
onClick
|
|
97
|
+
return openBlock(), createBlock(unref(VAppBar), {
|
|
98
|
+
title: props.title,
|
|
99
|
+
"data-testid": props.dataTestid
|
|
157
100
|
}, {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
101
|
+
prepend: withCtx(() => [__props.navigation ? (openBlock(), createBlock(unref(VAppBarNavIcon), {
|
|
102
|
+
key: 0,
|
|
103
|
+
"data-testid": navigationBtnDataTestid.value,
|
|
104
|
+
onClick: _cache[0] || (_cache[0] = ($event) => navigationOpen.value = !navigationOpen.value)
|
|
105
|
+
}, null, 8, ["data-testid"])) : createCommentVNode("v-if", true)]),
|
|
106
|
+
append: withCtx(() => [
|
|
107
|
+
showHelp.value ? (openBlock(), createBlock(unref(VBtn), mergeProps({ key: 0 }, unref(routeProps), {
|
|
108
|
+
"data-testid": helpBtnDataTestid.value,
|
|
109
|
+
icon: "mdi-help-circle-outline"
|
|
110
|
+
}), null, 16, ["data-testid"])) : createCommentVNode("v-if", true),
|
|
111
|
+
showNotifications.value ? (openBlock(), createBlock(unref(VMenu), {
|
|
112
|
+
key: 1,
|
|
113
|
+
modelValue: notificationsOpen.value,
|
|
114
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => notificationsOpen.value = $event),
|
|
115
|
+
"close-on-content-click": false
|
|
116
|
+
}, {
|
|
117
|
+
activator: withCtx(({ props }) => [createVNode(unref(VBtn), mergeProps(props, {
|
|
118
|
+
"data-testid": notificationsBtnDataTestid.value,
|
|
119
|
+
icon: ""
|
|
120
|
+
}), {
|
|
121
|
+
default: withCtx(() => [createVNode(unref(VBadge), {
|
|
122
|
+
"model-value": hasUnreadNotifications.value,
|
|
123
|
+
content: unreadCount.value,
|
|
124
|
+
color: "error"
|
|
125
|
+
}, {
|
|
126
|
+
default: withCtx(() => [createVNode(unref(VIcon), { icon: "mdi-bell-outline" })]),
|
|
127
|
+
_: 1
|
|
128
|
+
}, 8, ["model-value", "content"])]),
|
|
129
|
+
_: 1
|
|
130
|
+
}, 16, ["data-testid"])]),
|
|
131
|
+
default: withCtx(() => [createVNode(unref(VCard), { "data-testid": notificationsMenuDataTestid.value }, {
|
|
132
|
+
default: withCtx(() => [props.notifications?.length ? (openBlock(), createBlock(unref(VList), { key: 0 }, {
|
|
133
|
+
default: withCtx(() => [createVNode(unref(VListSubheader), null, {
|
|
134
|
+
default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode("Notificações", -1)])]),
|
|
135
|
+
_: 1
|
|
136
|
+
}), (openBlock(true), createElementBlock(Fragment, null, renderList(props.notifications, (notification, index) => {
|
|
137
|
+
return openBlock(), createBlock(unref(VListItem), {
|
|
138
|
+
key: index,
|
|
139
|
+
active: !notification.read,
|
|
140
|
+
title: notification.title,
|
|
141
|
+
subtitle: notification.subtitle,
|
|
142
|
+
"active-class": "text-primary"
|
|
143
|
+
}, createSlots({ _: 2 }, [notification.date ? {
|
|
144
|
+
name: "append",
|
|
145
|
+
fn: withCtx(() => [createVNode(unref(VListItemAction), { end: "" }, {
|
|
146
|
+
default: withCtx(() => [createElementVNode("small", _hoisted_1$1, toDisplayString(formatNotificationDate(notification.date)), 1)]),
|
|
147
|
+
_: 2
|
|
148
|
+
}, 1024)]),
|
|
149
|
+
key: "0"
|
|
150
|
+
} : void 0]), 1032, [
|
|
151
|
+
"active",
|
|
152
|
+
"title",
|
|
153
|
+
"subtitle"
|
|
154
|
+
]);
|
|
155
|
+
}), 128))]),
|
|
156
|
+
_: 1
|
|
157
|
+
})) : (openBlock(), createBlock(unref(VCardText), { key: 1 }, {
|
|
158
|
+
default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode("Nenhuma notificação", -1)])]),
|
|
159
|
+
_: 1
|
|
160
|
+
}))]),
|
|
161
|
+
_: 1
|
|
162
|
+
}, 8, ["data-testid"])]),
|
|
163
|
+
_: 1
|
|
164
|
+
}, 8, ["modelValue"])) : createCommentVNode("v-if", true),
|
|
165
|
+
showUser.value ? (openBlock(), createBlock(unref(VMenu), {
|
|
166
|
+
key: 2,
|
|
167
|
+
"close-on-content-click": false
|
|
168
|
+
}, {
|
|
169
|
+
activator: withCtx(({ props }) => [createVNode(unref(VBtn), mergeProps(props, {
|
|
170
|
+
"data-testid": userBtnDataTestid.value,
|
|
171
|
+
icon: ""
|
|
172
|
+
}), {
|
|
173
|
+
default: withCtx(() => [createVNode(unref(VAvatar), mergeProps(userAvatarProps.value, { color: "primary" }), null, 16)]),
|
|
174
|
+
_: 1
|
|
175
|
+
}, 16, ["data-testid"])]),
|
|
176
|
+
default: withCtx(() => [createVNode(unref(VCard), {
|
|
177
|
+
title: __props.user?.title,
|
|
178
|
+
subtitle: __props.user?.subtitle,
|
|
179
|
+
"data-testid": userMenuDataTestid.value
|
|
180
|
+
}, {
|
|
181
|
+
prepend: withCtx(() => [createVNode(unref(VAvatar), mergeProps(userAvatarProps.value, { color: "primary" }), null, 16)]),
|
|
182
|
+
default: withCtx(() => [createVNode(unref(VDivider)), createVNode(unref(VList), { nav: "" }, {
|
|
183
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.user?.options, (option, index) => {
|
|
184
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [index > 0 ? (openBlock(), createBlock(unref(VDivider), {
|
|
185
|
+
key: 0,
|
|
186
|
+
class: "mt-1"
|
|
187
|
+
})) : createCommentVNode("v-if", true), isParentOption(option) ? (openBlock(), createBlock(unref(VListGroup), { key: 1 }, {
|
|
188
|
+
activator: withCtx(({ props }) => [createVNode(unref(VListItem), mergeProps({ ref_for: true }, props, { title: option.description }), null, 16, ["title"])]),
|
|
189
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(option.items, (childOption, index) => {
|
|
190
|
+
return openBlock(), createBlock(unref(VListItem), mergeProps({ key: index }, { ref_for: true }, unref(routeToProps)(childOption.route), {
|
|
191
|
+
title: childOption.description,
|
|
192
|
+
onClick: childOption.action
|
|
193
|
+
}), null, 16, ["title", "onClick"]);
|
|
194
|
+
}), 128))]),
|
|
195
|
+
_: 2
|
|
196
|
+
}, 1024)) : (openBlock(), createBlock(unref(VListItem), mergeProps({
|
|
197
|
+
key: 2,
|
|
198
|
+
title: option.description
|
|
199
|
+
}, { ref_for: true }, unref(routeToProps)(option.route), { onClick: option.action }), null, 16, ["title", "onClick"]))], 64);
|
|
200
|
+
}), 128))]),
|
|
201
|
+
_: 1
|
|
202
|
+
})]),
|
|
203
|
+
_: 1
|
|
204
|
+
}, 8, [
|
|
205
|
+
"title",
|
|
206
|
+
"subtitle",
|
|
207
|
+
"data-testid"
|
|
208
|
+
])]),
|
|
209
|
+
_: 1
|
|
210
|
+
})) : createCommentVNode("v-if", true)
|
|
211
|
+
]),
|
|
212
|
+
_: 1
|
|
213
|
+
}, 8, ["title", "data-testid"]);
|
|
169
214
|
};
|
|
170
215
|
}
|
|
171
216
|
});
|
|
@@ -214,13 +259,13 @@ const textFieldTypeToVuetifyRule = {
|
|
|
214
259
|
function useTextFieldVariant(variant) {
|
|
215
260
|
return computed(() => {
|
|
216
261
|
const variantValue = toValue(variant);
|
|
217
|
-
return
|
|
262
|
+
return textFieldVariantToVuetifyVariant[variantValue];
|
|
218
263
|
});
|
|
219
264
|
}
|
|
220
265
|
function useTextFieldType(type) {
|
|
221
266
|
return computed(() => {
|
|
222
267
|
const typeValue = toValue(type);
|
|
223
|
-
return
|
|
268
|
+
return textFieldTypeToVuetifyType[typeValue];
|
|
224
269
|
});
|
|
225
270
|
}
|
|
226
271
|
function useTextFieldRules(props) {
|
|
@@ -243,11 +288,13 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
243
288
|
props: /*@__PURE__*/ mergeModels({
|
|
244
289
|
variant: {
|
|
245
290
|
type: String,
|
|
246
|
-
required: false
|
|
291
|
+
required: false,
|
|
292
|
+
default: "primary"
|
|
247
293
|
},
|
|
248
294
|
type: {
|
|
249
295
|
type: String,
|
|
250
|
-
required: false
|
|
296
|
+
required: false,
|
|
297
|
+
default: "text"
|
|
251
298
|
},
|
|
252
299
|
disabled: {
|
|
253
300
|
type: Boolean,
|
|
@@ -260,7 +307,7 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
260
307
|
clearable: {
|
|
261
308
|
type: Boolean,
|
|
262
309
|
required: false,
|
|
263
|
-
default:
|
|
310
|
+
default: void 0
|
|
264
311
|
},
|
|
265
312
|
readonly: {
|
|
266
313
|
type: Boolean,
|
|
@@ -296,17 +343,23 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
296
343
|
emits: ["update:modelValue"],
|
|
297
344
|
setup(__props) {
|
|
298
345
|
const modelValue = useModel(__props, "modelValue");
|
|
299
|
-
const props =
|
|
346
|
+
const props = __props;
|
|
300
347
|
const vuetifyVariant = useTextFieldVariant(() => props.variant);
|
|
301
348
|
const vuetifyType = useTextFieldType(() => props.type);
|
|
302
349
|
const rules = useTextFieldRules(props);
|
|
303
|
-
const
|
|
350
|
+
const isSearchField = computed(() => props.type === "search");
|
|
351
|
+
const computedClearable = computed(() => props.clearable ?? isSearchField.value);
|
|
352
|
+
const prependIcon = computed(() => isSearchField.value ? "mdi-magnify" : void 0);
|
|
353
|
+
function clear() {
|
|
354
|
+
modelValue.value = "";
|
|
355
|
+
}
|
|
304
356
|
return (_ctx, _cache) => {
|
|
305
357
|
return openBlock(), createBlock(unref(VTextField), {
|
|
306
358
|
modelValue: modelValue.value,
|
|
307
359
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
|
|
308
360
|
variant: unref(vuetifyVariant),
|
|
309
361
|
type: unref(vuetifyType),
|
|
362
|
+
"prepend-inner-icon": prependIcon.value,
|
|
310
363
|
rules: unref(rules),
|
|
311
364
|
"data-testid": props.dataTestid,
|
|
312
365
|
disabled: props.disabled,
|
|
@@ -315,11 +368,13 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
315
368
|
label: props.label,
|
|
316
369
|
placeholder: props.placeholder,
|
|
317
370
|
hint: props.hint,
|
|
318
|
-
clearable: computedClearable.value
|
|
371
|
+
clearable: computedClearable.value,
|
|
372
|
+
"onClick:clear": clear
|
|
319
373
|
}, null, 8, [
|
|
320
374
|
"modelValue",
|
|
321
375
|
"variant",
|
|
322
376
|
"type",
|
|
377
|
+
"prepend-inner-icon",
|
|
323
378
|
"rules",
|
|
324
379
|
"data-testid",
|
|
325
380
|
"disabled",
|
|
@@ -334,6 +389,297 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
334
389
|
}
|
|
335
390
|
});
|
|
336
391
|
//#endregion
|
|
392
|
+
//#region src/components/navigation-menu/navigation-menu.vue?vue&type=script&setup=true&lang.ts
|
|
393
|
+
const _hoisted_1 = { class: "px-4 pt-2" };
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region src/components/navigation-menu/navigation-menu.vue
|
|
396
|
+
var navigation_menu_default = /* @__PURE__ */ defineComponent({
|
|
397
|
+
inheritAttrs: false,
|
|
398
|
+
__name: "navigation-menu",
|
|
399
|
+
props: /*@__PURE__*/ mergeModels({
|
|
400
|
+
dataTestid: {
|
|
401
|
+
type: String,
|
|
402
|
+
required: false
|
|
403
|
+
},
|
|
404
|
+
items: {
|
|
405
|
+
type: Array,
|
|
406
|
+
required: false
|
|
407
|
+
}
|
|
408
|
+
}, {
|
|
409
|
+
"modelValue": {
|
|
410
|
+
type: Boolean,
|
|
411
|
+
default: false
|
|
412
|
+
},
|
|
413
|
+
"modelModifiers": {}
|
|
414
|
+
}),
|
|
415
|
+
emits: ["update:modelValue"],
|
|
416
|
+
setup(__props) {
|
|
417
|
+
const props = __props;
|
|
418
|
+
const open = useModel(__props, "modelValue");
|
|
419
|
+
const search = ref("");
|
|
420
|
+
const filteredItems = computed(() => filterItems());
|
|
421
|
+
const searchDataTestid = computed(() => `${props.dataTestid}-search`);
|
|
422
|
+
const query = computed(() => search.value.toLowerCase());
|
|
423
|
+
function filterItems() {
|
|
424
|
+
return (props.items ?? []).reduce((filtered, item) => {
|
|
425
|
+
if (matches(item)) filtered.push(item);
|
|
426
|
+
else if (isParentItem(item)) {
|
|
427
|
+
const matchingChildren = item.items.filter((child) => matches(child));
|
|
428
|
+
if (matchingChildren.length) filtered.push({
|
|
429
|
+
...item,
|
|
430
|
+
items: matchingChildren
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
return filtered;
|
|
434
|
+
}, []);
|
|
435
|
+
}
|
|
436
|
+
function matches(item) {
|
|
437
|
+
return item.description.toLowerCase().includes(query.value);
|
|
438
|
+
}
|
|
439
|
+
function isParentItem(item) {
|
|
440
|
+
return "items" in item;
|
|
441
|
+
}
|
|
442
|
+
return (_ctx, _cache) => {
|
|
443
|
+
return openBlock(), createBlock(unref(VNavigationDrawer), {
|
|
444
|
+
modelValue: open.value,
|
|
445
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => open.value = $event),
|
|
446
|
+
"data-testid": props.dataTestid,
|
|
447
|
+
absolute: "",
|
|
448
|
+
temporary: ""
|
|
449
|
+
}, {
|
|
450
|
+
prepend: withCtx(() => [createElementVNode("div", _hoisted_1, [createVNode(text_field_default, {
|
|
451
|
+
modelValue: search.value,
|
|
452
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
|
|
453
|
+
type: "search",
|
|
454
|
+
label: "Buscar",
|
|
455
|
+
"data-testid": searchDataTestid.value
|
|
456
|
+
}, null, 8, ["modelValue", "data-testid"])])]),
|
|
457
|
+
default: withCtx(() => [createVNode(unref(VDivider)), createVNode(unref(VList), { nav: "" }, {
|
|
458
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(filteredItems.value, (item, index) => {
|
|
459
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [isParentItem(item) ? (openBlock(), createBlock(unref(VListGroup), { key: 0 }, {
|
|
460
|
+
activator: withCtx(({ props: activatorProps }) => [createVNode(unref(VListItem), mergeProps({ ref_for: true }, activatorProps, { title: item.description }), null, 16, ["title"])]),
|
|
461
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(item.items, (childItem, childIndex) => {
|
|
462
|
+
return openBlock(), createBlock(unref(VListItem), mergeProps({ key: childIndex }, { ref_for: true }, unref(routeToProps)(childItem.route), {
|
|
463
|
+
title: childItem.description,
|
|
464
|
+
onClick: childItem.action
|
|
465
|
+
}), null, 16, ["title", "onClick"]);
|
|
466
|
+
}), 128))]),
|
|
467
|
+
_: 2
|
|
468
|
+
}, 1024)) : (openBlock(), createBlock(unref(VListItem), mergeProps({
|
|
469
|
+
key: 1,
|
|
470
|
+
title: item.description
|
|
471
|
+
}, { ref_for: true }, unref(routeToProps)(item.route), { onClick: item.action }), null, 16, ["title", "onClick"]))], 64);
|
|
472
|
+
}), 128))]),
|
|
473
|
+
_: 1
|
|
474
|
+
})]),
|
|
475
|
+
_: 1
|
|
476
|
+
}, 8, ["modelValue", "data-testid"]);
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region src/components/grid/container/container.vue
|
|
482
|
+
var container_default = /* @__PURE__ */ defineComponent({
|
|
483
|
+
inheritAttrs: false,
|
|
484
|
+
__name: "container",
|
|
485
|
+
props: { dataTestid: {
|
|
486
|
+
type: String,
|
|
487
|
+
required: false
|
|
488
|
+
} },
|
|
489
|
+
setup(__props) {
|
|
490
|
+
const props = __props;
|
|
491
|
+
return (_ctx, _cache) => {
|
|
492
|
+
return openBlock(), createBlock(unref(VContainer), {
|
|
493
|
+
"data-testid": props.dataTestid,
|
|
494
|
+
fluid: ""
|
|
495
|
+
}, {
|
|
496
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
497
|
+
_: 3
|
|
498
|
+
}, 8, ["data-testid"]);
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
//#endregion
|
|
503
|
+
//#region src/components/dialogs/toast.vue
|
|
504
|
+
var toast_default = /* @__PURE__ */ defineComponent({
|
|
505
|
+
inheritAttrs: false,
|
|
506
|
+
__name: "toast",
|
|
507
|
+
setup(__props) {
|
|
508
|
+
return (_ctx, _cache) => {
|
|
509
|
+
return openBlock(), createBlock(unref(VSnackbarQueue), {
|
|
510
|
+
modelValue: unref(messages),
|
|
511
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(messages) ? messages.value = $event : null),
|
|
512
|
+
"display-strategy": "overflow",
|
|
513
|
+
location: "top end",
|
|
514
|
+
"total-visible": "3",
|
|
515
|
+
collapsed: "",
|
|
516
|
+
closable: "",
|
|
517
|
+
timer: ""
|
|
518
|
+
}, {
|
|
519
|
+
actions: withCtx(({ props }) => [createVNode(unref(VIconBtn), mergeProps(props, {
|
|
520
|
+
"aria-label": "Fechar",
|
|
521
|
+
icon: "$close",
|
|
522
|
+
size: "small",
|
|
523
|
+
variant: "text"
|
|
524
|
+
}), null, 16)]),
|
|
525
|
+
_: 1
|
|
526
|
+
}, 8, ["modelValue"]);
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/components/root/root.vue
|
|
532
|
+
var root_default = /* @__PURE__ */ defineComponent({
|
|
533
|
+
inheritAttrs: false,
|
|
534
|
+
__name: "root",
|
|
535
|
+
props: /*@__PURE__*/ mergeModels({
|
|
536
|
+
dataTestid: {
|
|
537
|
+
type: String,
|
|
538
|
+
required: false
|
|
539
|
+
},
|
|
540
|
+
appBar: {
|
|
541
|
+
type: Object,
|
|
542
|
+
required: false
|
|
543
|
+
},
|
|
544
|
+
navigationMenu: {
|
|
545
|
+
type: Object,
|
|
546
|
+
required: false
|
|
547
|
+
}
|
|
548
|
+
}, {
|
|
549
|
+
"notificationsOpen": {
|
|
550
|
+
type: Boolean,
|
|
551
|
+
default: false
|
|
552
|
+
},
|
|
553
|
+
"notificationsOpenModifiers": {}
|
|
554
|
+
}),
|
|
555
|
+
emits: ["update:notificationsOpen"],
|
|
556
|
+
setup(__props) {
|
|
557
|
+
const props = __props;
|
|
558
|
+
const appBarProps = computed(() => props.appBar ? {
|
|
559
|
+
...props.appBar,
|
|
560
|
+
navigation: props.navigationMenu !== void 0
|
|
561
|
+
} : void 0);
|
|
562
|
+
const notificationsOpen = useModel(__props, "notificationsOpen");
|
|
563
|
+
const navigationMenuOpen = ref(false);
|
|
564
|
+
return (_ctx, _cache) => {
|
|
565
|
+
return openBlock(), createBlock(unref(VApp), { "data-testid": props.dataTestid }, {
|
|
566
|
+
default: withCtx(() => [
|
|
567
|
+
appBarProps.value ? (openBlock(), createBlock(app_bar_default, mergeProps({ key: 0 }, appBarProps.value, {
|
|
568
|
+
"notifications-open": notificationsOpen.value,
|
|
569
|
+
"onUpdate:notificationsOpen": _cache[0] || (_cache[0] = ($event) => notificationsOpen.value = $event),
|
|
570
|
+
"navigation-open": navigationMenuOpen.value,
|
|
571
|
+
"onUpdate:navigationOpen": _cache[1] || (_cache[1] = ($event) => navigationMenuOpen.value = $event)
|
|
572
|
+
}), null, 16, ["notifications-open", "navigation-open"])) : createCommentVNode("v-if", true),
|
|
573
|
+
props.navigationMenu ? (openBlock(), createBlock(navigation_menu_default, mergeProps({ key: 1 }, props.navigationMenu, {
|
|
574
|
+
modelValue: navigationMenuOpen.value,
|
|
575
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => navigationMenuOpen.value = $event)
|
|
576
|
+
}), null, 16, ["modelValue"])) : createCommentVNode("v-if", true),
|
|
577
|
+
createVNode(unref(VMain), null, {
|
|
578
|
+
default: withCtx(() => [createVNode(container_default, null, {
|
|
579
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default"), createVNode(toast_default)]),
|
|
580
|
+
_: 3
|
|
581
|
+
})]),
|
|
582
|
+
_: 3
|
|
583
|
+
})
|
|
584
|
+
]),
|
|
585
|
+
_: 3
|
|
586
|
+
}, 8, ["data-testid"]);
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
//#endregion
|
|
591
|
+
//#region src/composables/btn.ts
|
|
592
|
+
const btnVariantToVuetifyVariant = {
|
|
593
|
+
primary: "elevated",
|
|
594
|
+
secondary: "flat",
|
|
595
|
+
ghost: "outlined"
|
|
596
|
+
};
|
|
597
|
+
function useBtnVariant(variant) {
|
|
598
|
+
return computed(() => {
|
|
599
|
+
const variantValue = toValue(variant);
|
|
600
|
+
return variantValue ? btnVariantToVuetifyVariant[variantValue] : btnVariantToVuetifyVariant.primary;
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
function useBtnType(type) {
|
|
604
|
+
return computed(() => {
|
|
605
|
+
return toValue(type) ?? "button";
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
function useBtnForm(form) {
|
|
609
|
+
return computed(() => toValue(form));
|
|
610
|
+
}
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region src/components/btn/btn.vue
|
|
613
|
+
var btn_default = /* @__PURE__ */ defineComponent({
|
|
614
|
+
inheritAttrs: false,
|
|
615
|
+
__name: "btn",
|
|
616
|
+
props: {
|
|
617
|
+
variant: {
|
|
618
|
+
type: String,
|
|
619
|
+
required: false
|
|
620
|
+
},
|
|
621
|
+
type: {
|
|
622
|
+
type: String,
|
|
623
|
+
required: false
|
|
624
|
+
},
|
|
625
|
+
color: {
|
|
626
|
+
type: String,
|
|
627
|
+
required: false
|
|
628
|
+
},
|
|
629
|
+
disabled: {
|
|
630
|
+
type: Boolean,
|
|
631
|
+
required: false
|
|
632
|
+
},
|
|
633
|
+
loading: {
|
|
634
|
+
type: Boolean,
|
|
635
|
+
required: false
|
|
636
|
+
},
|
|
637
|
+
form: {
|
|
638
|
+
type: String,
|
|
639
|
+
required: false
|
|
640
|
+
},
|
|
641
|
+
dataTestid: {
|
|
642
|
+
type: String,
|
|
643
|
+
required: false
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
emits: ["click"],
|
|
647
|
+
setup(__props, { emit: __emit }) {
|
|
648
|
+
const props = __props;
|
|
649
|
+
const emit = __emit;
|
|
650
|
+
const variant = useBtnVariant(() => props.variant);
|
|
651
|
+
const color = useVuetifyColor(() => props.color);
|
|
652
|
+
const type = useBtnType(() => props.type);
|
|
653
|
+
const form = useBtnForm(() => props.form);
|
|
654
|
+
function onClick(event) {
|
|
655
|
+
emit("click", event);
|
|
656
|
+
}
|
|
657
|
+
return (_ctx, _cache) => {
|
|
658
|
+
return openBlock(), createBlock(unref(VBtn), {
|
|
659
|
+
disabled: props.disabled,
|
|
660
|
+
loading: props.loading,
|
|
661
|
+
"data-testid": props.dataTestid,
|
|
662
|
+
variant: unref(variant),
|
|
663
|
+
color: unref(color),
|
|
664
|
+
type: unref(type),
|
|
665
|
+
form: unref(form),
|
|
666
|
+
onClick
|
|
667
|
+
}, {
|
|
668
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
669
|
+
_: 3
|
|
670
|
+
}, 8, [
|
|
671
|
+
"disabled",
|
|
672
|
+
"loading",
|
|
673
|
+
"data-testid",
|
|
674
|
+
"variant",
|
|
675
|
+
"color",
|
|
676
|
+
"type",
|
|
677
|
+
"form"
|
|
678
|
+
]);
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
//#endregion
|
|
337
683
|
//#region src/components/grid/row/row.vue
|
|
338
684
|
var row_default = /* @__PURE__ */ defineComponent({
|
|
339
685
|
inheritAttrs: false,
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import "vuetify/styles";
|
|
|
15
15
|
import { Plugin } from "vue";
|
|
16
16
|
//#region src/index.d.ts
|
|
17
17
|
/**
|
|
18
|
-
* Create an
|
|
18
|
+
* Create an UIMed-Vue instance to be installed after [createApp](https://vuejs.org/guide/essentials/application.html#the-application-instance).
|
|
19
19
|
* @returns an instance to be used with [app.use](https://vuejs.org/guide/essentials/application.html#the-application-instance)
|
|
20
20
|
*/
|
|
21
21
|
declare function createUimed(): Plugin;
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createVuetify } from "vuetify";
|
|
|
16
16
|
import "vue";
|
|
17
17
|
//#region src/index.ts
|
|
18
18
|
/**
|
|
19
|
-
* Create an
|
|
19
|
+
* Create an UIMed-Vue instance to be installed after [createApp](https://vuejs.org/guide/essentials/application.html#the-application-instance).
|
|
20
20
|
* @returns an instance to be used with [app.use](https://vuejs.org/guide/essentials/application.html#the-application-instance)
|
|
21
21
|
*/
|
|
22
22
|
function createUimed() {
|
package/dist/unit-test.js
CHANGED
|
@@ -16,7 +16,7 @@ const vuetify = createVuetify({
|
|
|
16
16
|
* Vue Test Utils plugin for Uimed.
|
|
17
17
|
*/
|
|
18
18
|
function vueTestUtilsPluginUimed() {
|
|
19
|
-
|
|
19
|
+
globalThis.ResizeObserver = __require("resize-observer-polyfill");
|
|
20
20
|
return vuetify;
|
|
21
21
|
}
|
|
22
22
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexdom/uimed-vue",
|
|
3
|
-
"version": "1.2.0-beta.
|
|
3
|
+
"version": "1.2.0-beta.3",
|
|
4
4
|
"description": "A ui framework for NEXDOM applications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lib",
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"home": "https://github.com/nexdom-healthtech/uimed-vue#readme"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
|
-
"dist"
|
|
24
|
+
"dist",
|
|
25
|
+
"AGENTS.md",
|
|
26
|
+
"CLAUDE.md"
|
|
25
27
|
],
|
|
26
28
|
"type": "module",
|
|
27
29
|
"exports": {
|
|
@@ -57,6 +59,7 @@
|
|
|
57
59
|
"@mdi/font": "^7.4.47",
|
|
58
60
|
"@nexdom/shared": "^2.1.1",
|
|
59
61
|
"@playwright/test": "^1.62.1",
|
|
62
|
+
"@sonar/scan": "^5.0.0",
|
|
60
63
|
"@stryker-mutator/core": "^10.0.0",
|
|
61
64
|
"@stryker-mutator/vitest-runner": "^10.0.0",
|
|
62
65
|
"@types/node": "^26.1.2",
|
|
@@ -79,6 +82,7 @@
|
|
|
79
82
|
"vitepress": "2.0.0-alpha.19",
|
|
80
83
|
"vitepress-plugin-group-icons": "^1.7.6",
|
|
81
84
|
"vue": "^3.5.40",
|
|
85
|
+
"vue-router": "^5.3.1",
|
|
82
86
|
"vue-tsc": "^3.3.9",
|
|
83
87
|
"vuetify": "^4.2.0"
|
|
84
88
|
},
|
|
@@ -90,6 +94,7 @@
|
|
|
90
94
|
"vite-plugin-vuetify": "^2.1.3",
|
|
91
95
|
"vite-plus": "^0.2.7",
|
|
92
96
|
"vue": "^3.5.40",
|
|
97
|
+
"vue-router": "^5.3.1",
|
|
93
98
|
"vuetify": "^4.2.0"
|
|
94
99
|
},
|
|
95
100
|
"commitlint": {
|