@nexdom/uimed-vue 2.0.0-beta.8 → 2.0.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.
Files changed (222) hide show
  1. package/README.md +1 -1
  2. package/dist/_virtual/_rolldown/runtime.js +7 -0
  3. package/dist/components/app-bar/app-bar.js +5 -0
  4. package/dist/components/app-bar/app-bar.vue_vue_type_script_setup_true_lang.js +286 -0
  5. package/dist/components/app-bar/types.d.ts +96 -0
  6. package/dist/components/button/button.js +5 -0
  7. package/dist/components/button/button.vue.d.ts +31 -0
  8. package/dist/components/button/button.vue_vue_type_script_setup_true_lang.js +97 -0
  9. package/dist/components/button/types.d.ts +65 -0
  10. package/dist/components/data-sets/data-set/data-set.js +5 -0
  11. package/dist/components/data-sets/data-set/data-set.vue.d.ts +47 -0
  12. package/dist/components/data-sets/data-set/data-set.vue_vue_type_script_setup_true_lang.js +157 -0
  13. package/dist/components/data-sets/data-set/types.d.ts +93 -0
  14. package/dist/components/data-sets/data-set-bar.js +5 -0
  15. package/dist/components/data-sets/data-set-bar.vue_vue_type_script_setup_true_lang.js +99 -0
  16. package/dist/components/data-sets/data-set-item/data-set-item.js +5 -0
  17. package/dist/components/data-sets/data-set-item/data-set-item.vue.d.ts +35 -0
  18. package/dist/components/data-sets/data-set-item/data-set-item.vue_vue_type_script_setup_true_lang.js +57 -0
  19. package/dist/components/data-sets/data-set-item/types.d.ts +42 -0
  20. package/dist/components/details/details.js +5 -0
  21. package/dist/components/details/details.vue.d.ts +28 -0
  22. package/dist/components/details/details.vue_vue_type_script_setup_true_lang.js +56 -0
  23. package/dist/components/details/types.d.ts +29 -0
  24. package/dist/components/dialogs/dialog/dialog-host-key.js +10 -0
  25. package/dist/components/dialogs/dialog/dialog.js +5 -0
  26. package/dist/components/dialogs/dialog/dialog.vue.d.ts +36 -0
  27. package/dist/components/dialogs/dialog/dialog.vue_vue_type_script_setup_true_lang.js +112 -0
  28. package/dist/components/dialogs/dialog/types.d.ts +47 -0
  29. package/dist/components/dialogs/dialog-host.js +5 -0
  30. package/dist/components/dialogs/dialog-host.vue_vue_type_script_setup_true_lang.js +81 -0
  31. package/dist/components/dialogs/toast.js +5 -0
  32. package/dist/components/dialogs/toast.vue_vue_type_script_setup_true_lang.js +35 -0
  33. package/dist/components/footer/footer.js +5 -0
  34. package/dist/components/footer/footer.vue_vue_type_script_setup_true_lang.js +31 -0
  35. package/dist/components/footer/types.d.ts +16 -0
  36. package/dist/components/form/form.js +5 -0
  37. package/dist/components/form/form.vue.d.ts +32 -0
  38. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +37 -0
  39. package/dist/components/form/types.d.ts +15 -0
  40. package/dist/components/grid/column/column.js +5 -0
  41. package/dist/components/grid/column/column.vue.d.ts +33 -0
  42. package/dist/components/grid/column/column.vue_vue_type_script_setup_true_lang.js +45 -0
  43. package/dist/components/grid/column/types.d.ts +27 -0
  44. package/dist/components/grid/container/container.js +5 -0
  45. package/dist/components/grid/container/container.vue.d.ts +27 -0
  46. package/dist/components/grid/container/container.vue_vue_type_script_setup_true_lang.js +33 -0
  47. package/dist/components/grid/container/types.d.ts +17 -0
  48. package/dist/components/grid/row/row.js +5 -0
  49. package/dist/components/grid/row/row.vue.d.ts +29 -0
  50. package/dist/components/grid/row/row.vue_vue_type_script_setup_true_lang.js +61 -0
  51. package/dist/components/grid/row/types.d.ts +63 -0
  52. package/dist/components/icon-button/icon-button.js +5 -0
  53. package/dist/components/icon-button/icon-button.vue.d.ts +23 -0
  54. package/dist/components/icon-button/icon-button.vue_vue_type_script_setup_true_lang.js +51 -0
  55. package/dist/components/icon-button/types.d.ts +32 -0
  56. package/dist/components/img/img.js +5 -0
  57. package/dist/components/img/img.vue.d.ts +23 -0
  58. package/dist/components/img/img.vue_vue_type_script_setup_true_lang.js +61 -0
  59. package/dist/components/img/types.d.ts +43 -0
  60. package/dist/components/inputs/autocomplete-field/autocomplete-field.js +5 -0
  61. package/dist/components/inputs/autocomplete-field/autocomplete-field.vue.d.ts +33 -0
  62. package/dist/components/inputs/autocomplete-field/autocomplete-field.vue_vue_type_script_setup_true_lang.js +161 -0
  63. package/dist/components/inputs/autocomplete-field/types.d.ts +84 -0
  64. package/dist/components/inputs/checkbox/checkbox.js +5 -0
  65. package/dist/components/inputs/checkbox/checkbox.vue.d.ts +33 -0
  66. package/dist/components/inputs/checkbox/checkbox.vue_vue_type_script_setup_true_lang.js +71 -0
  67. package/dist/components/inputs/checkbox/types.d.ts +35 -0
  68. package/dist/components/inputs/date-time-field/date-time-field.js +5 -0
  69. package/dist/components/inputs/date-time-field/date-time-field.vue.d.ts +32 -0
  70. package/dist/components/inputs/date-time-field/date-time-field.vue_vue_type_script_setup_true_lang.js +205 -0
  71. package/dist/components/inputs/date-time-field/types.d.ts +84 -0
  72. package/dist/components/inputs/field-loader.js +5 -0
  73. package/dist/components/inputs/field-loader.vue_vue_type_script_setup_true_lang.js +30 -0
  74. package/dist/components/inputs/mask-field/mask-field.js +5 -0
  75. package/dist/components/inputs/mask-field/mask-field.vue.d.ts +32 -0
  76. package/dist/components/inputs/mask-field/mask-field.vue_vue_type_script_setup_true_lang.js +120 -0
  77. package/dist/components/inputs/mask-field/types.d.ts +118 -0
  78. package/dist/components/inputs/text-field/text-field.js +5 -0
  79. package/dist/components/inputs/text-field/text-field.vue.d.ts +30 -0
  80. package/dist/components/inputs/text-field/text-field.vue_vue_type_script_setup_true_lang.js +119 -0
  81. package/dist/components/inputs/text-field/types.d.ts +70 -0
  82. package/dist/components/link/link.js +5 -0
  83. package/dist/components/link/link.vue.d.ts +27 -0
  84. package/dist/components/link/link.vue_vue_type_script_setup_true_lang.js +32 -0
  85. package/dist/components/link/types.d.ts +18 -0
  86. package/dist/components/main/main.js +5 -0
  87. package/dist/components/main/main.vue.d.ts +33 -0
  88. package/dist/components/main/main.vue_vue_type_script_setup_true_lang.js +104 -0
  89. package/dist/components/main/types.d.ts +75 -0
  90. package/dist/components/navigation-menu/navigation-menu.js +5 -0
  91. package/dist/components/navigation-menu/navigation-menu.vue_vue_type_script_setup_true_lang.js +207 -0
  92. package/dist/components/navigation-menu/types.d.ts +62 -0
  93. package/dist/components/page-transition/page-transition.js +5 -0
  94. package/dist/components/page-transition/page-transition.vue.d.ts +40 -0
  95. package/dist/components/page-transition/page-transition.vue_vue_type_script_setup_true_lang.js +27 -0
  96. package/dist/components/page-transition/types.d.ts +18 -0
  97. package/dist/components/sections/section/section.js +5 -0
  98. package/dist/components/sections/section/section.vue.d.ts +28 -0
  99. package/dist/components/sections/section/section.vue_vue_type_script_setup_true_lang.js +108 -0
  100. package/dist/components/sections/section/types.d.ts +80 -0
  101. package/dist/components/sections/section-content/section-content.js +5 -0
  102. package/dist/components/sections/section-content/section-content.vue.d.ts +27 -0
  103. package/dist/components/sections/section-content/section-content.vue_vue_type_script_setup_true_lang.js +16 -0
  104. package/dist/components/skeleton-loader/skeleton-loader.js +5 -0
  105. package/dist/components/skeleton-loader/skeleton-loader.vue_vue_type_script_setup_true_lang.js +65 -0
  106. package/dist/components/table/table.js +5 -0
  107. package/dist/components/table/table.vue.d.ts +19 -0
  108. package/dist/components/table/table.vue_vue_type_script_setup_true_lang.js +84 -0
  109. package/dist/components/table/types.d.ts +51 -0
  110. package/dist/components.d.ts +23 -940
  111. package/dist/components.js +23 -1254
  112. package/dist/composables/alignment/alignment.js +41 -0
  113. package/dist/composables/alignment/types.d.ts +12 -0
  114. package/dist/composables/button/button.js +54 -0
  115. package/dist/composables/colors/constants.js +27 -0
  116. package/dist/composables/colors/types.d.ts +4 -0
  117. package/dist/composables/colors/use-vuetify-background-color.js +16 -0
  118. package/dist/composables/colors/use-vuetify-color.js +11 -0
  119. package/dist/composables/colors/use-vuetify-text-color.js +12 -0
  120. package/dist/composables/data-set/data-set.js +128 -0
  121. package/dist/composables/dialogs/types.d.ts +88 -0
  122. package/dist/composables/dialogs/use-confirm.d.ts +21 -0
  123. package/dist/composables/dialogs/use-confirm.js +55 -0
  124. package/dist/composables/dialogs/use-decorative-timer.js +25 -0
  125. package/dist/composables/dialogs/use-dialog-focus.js +64 -0
  126. package/dist/composables/dialogs/use-dialog-loading.js +13 -0
  127. package/dist/composables/dialogs/use-dialog.d.ts +36 -0
  128. package/dist/composables/dialogs/use-dialog.js +73 -0
  129. package/dist/composables/dialogs/use-run-or-toast.d.ts +27 -0
  130. package/dist/composables/dialogs/use-run-or-toast.js +65 -0
  131. package/dist/composables/dialogs/use-toast.d.ts +25 -0
  132. package/dist/composables/dialogs/use-toast.js +39 -0
  133. package/dist/composables/dimensions/dimensions.js +18 -0
  134. package/dist/composables/inputs/autocomplete-field.js +39 -0
  135. package/dist/composables/inputs/date-time-field.js +109 -0
  136. package/dist/composables/inputs/fields.js +28 -0
  137. package/dist/composables/inputs/mask-field.js +133 -0
  138. package/dist/composables/inputs/rules.js +16 -0
  139. package/dist/composables/inputs/text-field.js +81 -0
  140. package/dist/composables/navigation/types.d.ts +54 -0
  141. package/dist/composables/navigation/use-navigation-rail.js +163 -0
  142. package/dist/composables/navigation/use-navigation-search-shortcut.js +96 -0
  143. package/dist/composables/navigation/use-route-props.js +21 -0
  144. package/dist/composables/navigation/use-unsaved-changes.d.ts +35 -0
  145. package/dist/composables/navigation/use-unsaved-changes.js +88 -0
  146. package/dist/composables/section/section.js +34 -0
  147. package/dist/composables.d.ts +6 -67
  148. package/dist/composables.js +6 -65
  149. package/dist/consts/icons.d.ts +67 -0
  150. package/dist/consts/icons.js +69 -0
  151. package/dist/consts/locale.js +12 -0
  152. package/dist/consts/types.d.ts +12 -0
  153. package/dist/index.d.ts +1 -13
  154. package/dist/index.js +39 -14
  155. package/dist/plugins.d.ts +15 -4
  156. package/dist/plugins.js +54 -3
  157. package/dist/styles/fonts/UnimedSlab-Regular.woff2 +0 -0
  158. package/dist/styles/fonts/fonts.css +5 -0
  159. package/dist/styles/settings.scss +155 -0
  160. package/dist/unit-test.js +11 -7
  161. package/package.json +11 -11
  162. package/skills/uimed-vue/SKILL.md +113 -0
  163. package/skills/uimed-vue/references/api/components/autocomplete-field.md +45 -0
  164. package/skills/uimed-vue/references/api/components/button.md +48 -0
  165. package/skills/uimed-vue/references/api/components/checkbox.md +36 -0
  166. package/skills/uimed-vue/references/api/components/data-sets/data-set-item.md +61 -0
  167. package/skills/uimed-vue/references/api/components/data-sets/data-set.md +67 -0
  168. package/skills/uimed-vue/references/api/components/date-time-field.md +44 -0
  169. package/skills/uimed-vue/references/api/components/details.md +33 -0
  170. package/skills/uimed-vue/references/api/components/dialog.md +70 -0
  171. package/skills/uimed-vue/references/api/components/form.md +43 -0
  172. package/skills/uimed-vue/references/api/components/grid/column.md +41 -0
  173. package/skills/uimed-vue/references/api/components/grid/container.md +32 -0
  174. package/skills/uimed-vue/references/api/components/grid/row.md +37 -0
  175. package/skills/uimed-vue/references/api/components/icon-button.md +37 -0
  176. package/skills/uimed-vue/references/api/components/img.md +34 -0
  177. package/skills/uimed-vue/references/api/components/link.md +31 -0
  178. package/skills/uimed-vue/references/api/components/main.md +174 -0
  179. package/skills/uimed-vue/references/api/components/mask-field.md +60 -0
  180. package/skills/uimed-vue/references/api/components/page-transition.md +37 -0
  181. package/skills/uimed-vue/references/api/components/sections/section-content.md +23 -0
  182. package/skills/uimed-vue/references/api/components/sections/section.md +61 -0
  183. package/skills/uimed-vue/references/api/components/table.md +35 -0
  184. package/skills/uimed-vue/references/api/components/text-field.md +42 -0
  185. package/skills/uimed-vue/references/api/composables/use-confirm.md +96 -0
  186. package/skills/uimed-vue/references/api/composables/use-dialog.md +95 -0
  187. package/skills/uimed-vue/references/api/composables/use-run-or-toast.md +58 -0
  188. package/skills/uimed-vue/references/api/composables/use-toast.md +48 -0
  189. package/skills/uimed-vue/references/api/composables/use-unsaved-changes.md +106 -0
  190. package/skills/uimed-vue/references/api/index.md +1 -0
  191. package/skills/uimed-vue/references/guide/components/autocomplete-field.md +256 -0
  192. package/skills/uimed-vue/references/guide/components/button.md +155 -0
  193. package/skills/uimed-vue/references/guide/components/checkbox.md +113 -0
  194. package/skills/uimed-vue/references/guide/components/data-set.md +370 -0
  195. package/skills/uimed-vue/references/guide/components/date-time-field.md +202 -0
  196. package/skills/uimed-vue/references/guide/components/details.md +76 -0
  197. package/skills/uimed-vue/references/guide/components/dialog.md +269 -0
  198. package/skills/uimed-vue/references/guide/components/form.md +96 -0
  199. package/skills/uimed-vue/references/guide/components/icon-button.md +76 -0
  200. package/skills/uimed-vue/references/guide/components/img.md +128 -0
  201. package/skills/uimed-vue/references/guide/components/layout.md +323 -0
  202. package/skills/uimed-vue/references/guide/components/link.md +47 -0
  203. package/skills/uimed-vue/references/guide/components/main.md +435 -0
  204. package/skills/uimed-vue/references/guide/components/mask-field.md +256 -0
  205. package/skills/uimed-vue/references/guide/components/page-transition.md +111 -0
  206. package/skills/uimed-vue/references/guide/components/section.md +232 -0
  207. package/skills/uimed-vue/references/guide/components/table.md +102 -0
  208. package/skills/uimed-vue/references/guide/components/text-field.md +172 -0
  209. package/skills/uimed-vue/references/guide/composables/use-confirm.md +123 -0
  210. package/skills/uimed-vue/references/guide/composables/use-dialog.md +102 -0
  211. package/skills/uimed-vue/references/guide/composables/use-run-or-toast.md +55 -0
  212. package/skills/uimed-vue/references/guide/composables/use-toast.md +57 -0
  213. package/skills/uimed-vue/references/guide/composables/use-unsaved-changes.md +129 -0
  214. package/skills/uimed-vue/references/guide/getting-started.md +135 -0
  215. package/skills/uimed-vue/references/guide/icons.md +71 -0
  216. package/skills/uimed-vue/references/guide/index.md +51 -0
  217. package/skills/uimed-vue/references/guide/migration-v1-to-v2.md +191 -0
  218. package/skills/uimed-vue/references/guide/unit-testing.md +85 -0
  219. package/AGENTS.md +0 -160
  220. package/CLAUDE.md +0 -1
  221. package/dist/types-BPXXtbmW.d.ts +0 -5
  222. package/dist/use-toast-CACuYhK_.js +0 -53
@@ -0,0 +1,73 @@
1
+ import { ref } from "vue";
2
+ //#region src/composables/dialogs/use-dialog.ts
3
+ /**
4
+ * Dialogs waiting to be displayed, shared by `useDialog` and `useConfirm`. The first one is the
5
+ * dialog on display (or about to be).
6
+ */
7
+ const requests = ref([]);
8
+ /**
9
+ * Mounted `Dialog` components, in mounting order. Only the first one displays dialogs, so pages
10
+ * with more than one `UMain` don't display duplicates.
11
+ */
12
+ const owners = ref([]);
13
+ /**
14
+ * Queues a dialog and resolves with what `onSelect` returns for the clicked action's value
15
+ * (`undefined` when the dialog is dismissed).
16
+ */
17
+ function openDialog(options, onSelect) {
18
+ const { actions, ...content } = options;
19
+ const lastIndex = actions.length - 1;
20
+ return new Promise((resolve) => {
21
+ requests.value.push({
22
+ ...content,
23
+ actions: actions.map((action, index) => ({
24
+ ...action,
25
+ variant: action.variant ?? (index === lastIndex ? "primary" : "ghost")
26
+ })),
27
+ async select(value) {
28
+ resolve(await onSelect(value));
29
+ }
30
+ });
31
+ });
32
+ }
33
+ /**
34
+ * Composable which returns a function to display dialogs.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const { dialog } = useDialog();
39
+ *
40
+ * const answer = await dialog({
41
+ * title: "Sessão expirando",
42
+ * message: "Deseja continuar conectado?",
43
+ * actions: [
44
+ * { text: "Sair", value: "leave" },
45
+ * { text: "Continuar", value: "stay" },
46
+ * ],
47
+ * });
48
+ * ```
49
+ */
50
+ function useDialog() {
51
+ return { dialog };
52
+ }
53
+ /**
54
+ * Displays a dialog. Dialogs are displayed one at a time, in the order they were requested.
55
+ * @param {DialogOptions} options - Options for the dialog.
56
+ * @returns the `value` of the clicked action, or `undefined` when the dialog is closed by the
57
+ * default action, the `Esc` key, a click outside it or the browser's back button.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * await dialog({ title: "Aviso", message: "Seu cadastro foi enviado para análise." });
62
+ * ```
63
+ */
64
+ function dialog(options) {
65
+ return openDialog({
66
+ title: options.title,
67
+ message: options.message,
68
+ role: "dialog",
69
+ actions: options.actions ?? [{ text: "Fechar" }]
70
+ }, (value) => value);
71
+ }
72
+ //#endregion
73
+ export { useDialog as default, openDialog, owners, requests };
@@ -0,0 +1,27 @@
1
+ //#region src/composables/dialogs/use-run-or-toast.d.ts
2
+ /**
3
+ * Generate `run` method and a computed property `isRunning`.
4
+ * Calling `run` will reactively update `isRunning`.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const { run, isRunning } = useRunOrToast();
9
+ *
10
+ * // Logs: false
11
+ * console.log(isRunning.value);
12
+ *
13
+ * const promise = run(() => someFunction(param1, param2));
14
+ *
15
+ * // Logs: true
16
+ * console.log(isRunning.value);
17
+ *
18
+ * await promise;
19
+ * // Logs: false
20
+ * console.log(isRunning.value);
21
+ * ```
22
+ */
23
+ export default function useRunOrToast(): {
24
+ run: <T extends (...args: Parameters<T>) => PromiseLike<Awaited<ReturnType<T>>>>(toRun: T, ...params: Parameters<T>) => Promise<Awaited<ReturnType<T>> | false>;
25
+ isRunning: import("vue").ComputedRef<boolean>;
26
+ };
27
+ //#endregion
@@ -0,0 +1,65 @@
1
+ import useToast from "./use-toast.js";
2
+ import { computed, ref } from "vue";
3
+ //#region src/composables/dialogs/use-run-or-toast.ts
4
+ const { toast } = useToast();
5
+ /**
6
+ * Generate `run` method and a computed property `isRunning`.
7
+ * Calling `run` will reactively update `isRunning`.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const { run, isRunning } = useRunOrToast();
12
+ *
13
+ * // Logs: false
14
+ * console.log(isRunning.value);
15
+ *
16
+ * const promise = run(() => someFunction(param1, param2));
17
+ *
18
+ * // Logs: true
19
+ * console.log(isRunning.value);
20
+ *
21
+ * await promise;
22
+ * // Logs: false
23
+ * console.log(isRunning.value);
24
+ * ```
25
+ */
26
+ function useRunOrToast() {
27
+ const running = ref(false);
28
+ const isRunning = computed(() => running.value);
29
+ /**
30
+ * Receives a function and its parameters, runs it, and if it throws an error, shows a toast with the error message.
31
+ * @returns the function returned value or `false` on error
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const { run, isRunning } = useRunOrToast();
36
+ *
37
+ * // Call like this:
38
+ * const someFunctionResult1 = await run(() => someFunction(param1, param2));
39
+ *
40
+ * // Or this:
41
+ * const someFunctionResult2 = await run(someFunction, param1, param2);
42
+ * ```
43
+ */
44
+ async function run(toRun, ...params) {
45
+ try {
46
+ running.value = true;
47
+ return await toRun(...params);
48
+ } catch (error) {
49
+ if (error instanceof Error) toast({
50
+ message: error.message,
51
+ color: "danger"
52
+ });
53
+ else console.error(error);
54
+ return false;
55
+ } finally {
56
+ running.value = false;
57
+ }
58
+ }
59
+ return {
60
+ run,
61
+ isRunning
62
+ };
63
+ }
64
+ //#endregion
65
+ export { useRunOrToast as default };
@@ -0,0 +1,25 @@
1
+ import { ToastOptions } from "./types.js";
2
+ //#region src/composables/dialogs/use-toast.d.ts
3
+ /**
4
+ * Composable which returns a function to display toast messages.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const { toast } = useToast();
9
+ * toast({ message: "Hello, World!" });
10
+ * ```
11
+ */
12
+ export default function useToast(): {
13
+ toast: typeof toast;
14
+ };
15
+ /**
16
+ * Displays a toast message.
17
+ * @param {ToastOptions} options - Options for the toast message.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * toast({ message: "Hello, World!" });
22
+ * ```
23
+ */
24
+ declare function toast(options: ToastOptions): void;
25
+ //#endregion
@@ -0,0 +1,39 @@
1
+ import { icons } from "../../consts/icons.js";
2
+ import { feedbackColorToVuetifyColor } from "../colors/constants.js";
3
+ import { ref } from "vue";
4
+ //#region src/composables/dialogs/use-toast.ts
5
+ const messages = ref([]);
6
+ /**
7
+ * Composable which returns a function to display toast messages.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const { toast } = useToast();
12
+ * toast({ message: "Hello, World!" });
13
+ * ```
14
+ */
15
+ function useToast() {
16
+ return { toast };
17
+ }
18
+ /**
19
+ * Displays a toast message.
20
+ * @param {ToastOptions} options - Options for the toast message.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * toast({ message: "Hello, World!" });
25
+ * ```
26
+ */
27
+ function toast(options) {
28
+ const text = options.message;
29
+ const color = feedbackColorToVuetifyColor[options.color ?? "informative"];
30
+ const toastMessage = {
31
+ text,
32
+ color,
33
+ prependIcon: icons[color],
34
+ timerColor: `rgb(var(--v-theme-on-${color}))`
35
+ };
36
+ messages.value.push(toastMessage);
37
+ }
38
+ //#endregion
39
+ export { useToast as default, messages };
@@ -0,0 +1,18 @@
1
+ import { computed, toValue } from "vue";
2
+ //#region src/composables/dimensions/dimensions.ts
3
+ /**
4
+ * Vuetify's utility class that stretches a component to the whole width of its parent, for
5
+ * components with a `fullWidth` prop.
6
+ */
7
+ function useFullWidth(fullWidth) {
8
+ return computed(() => toValue(fullWidth) ? "w-100" : void 0);
9
+ }
10
+ /**
11
+ * Vuetify's utility class that stretches a component to the whole height of its parent, for
12
+ * components with a `fullHeight` prop. The parent must have a height of its own.
13
+ */
14
+ function useFullHeight(fullHeight) {
15
+ return computed(() => toValue(fullHeight) ? "h-100" : void 0);
16
+ }
17
+ //#endregion
18
+ export { useFullHeight, useFullWidth };
@@ -0,0 +1,39 @@
1
+ import { createRequiredRule } from "./fields.js";
2
+ import { computed, toValue } from "vue";
3
+ import { isEmpty } from "@nexdom/shared/utils";
4
+ //#region src/composables/inputs/autocomplete-field.ts
5
+ function createAutocompleteRequiredRule(options) {
6
+ return createRequiredRule(options.required, (value) => {
7
+ if (options.multiple) return (Array.isArray(value) ? value : []).length > 0;
8
+ return !isEmpty(String(value ?? ""));
9
+ });
10
+ }
11
+ function useAutocompleteRules(options) {
12
+ return computed(() => {
13
+ const opts = toValue(options);
14
+ const rules = [];
15
+ if (opts.required) rules.push(createAutocompleteRequiredRule(opts));
16
+ return rules;
17
+ });
18
+ }
19
+ /**
20
+ * Menu props that keep the combobox semantics only on the field's `<input>`.
21
+ *
22
+ * The field's container gets `role="combobox"` from the field, which passes its
23
+ * role to both the container and the input, and the menu's ARIA attributes,
24
+ * since it's the menu's activator. Without these props, the container is
25
+ * exposed as an unnamed combobox wrapping the input. The menu removes from its
26
+ * activator the attributes set to `undefined`, role included, and the field
27
+ * never sets that role again, since its value doesn't change. The input keeps
28
+ * its role, label, `aria-expanded` and `aria-controls`. This only happens in
29
+ * the browser: server-rendered HTML still has the role on the container.
30
+ */
31
+ const autocompleteFieldMenuProps = { activatorProps: {
32
+ role: void 0,
33
+ "aria-haspopup": void 0,
34
+ "aria-expanded": void 0,
35
+ "aria-controls": void 0,
36
+ "aria-owns": void 0
37
+ } };
38
+ //#endregion
39
+ export { autocompleteFieldMenuProps, createAutocompleteRequiredRule, useAutocompleteRules };
@@ -0,0 +1,109 @@
1
+ import { useRules } from "./fields.js";
2
+ import { computed, toValue, useId } from "vue";
3
+ //#region src/composables/inputs/date-time-field.ts
4
+ /**
5
+ * The menu's content is a dialog with the pickers. `aria-owns` is removed since the input can't
6
+ * own the content, and the menu still adds `aria-expanded` and `aria-controls`.
7
+ */
8
+ const activatorProps = {
9
+ role: "combobox",
10
+ "aria-haspopup": "dialog",
11
+ "aria-owns": void 0
12
+ };
13
+ /**
14
+ * Extracts the date part (`YYYY-MM-DD`) of a value, or an empty string when
15
+ * the type has no date.
16
+ */
17
+ function getDatePart(value, type) {
18
+ return type === "time" ? "" : value.split("T")[0];
19
+ }
20
+ /**
21
+ * Extracts the time part (`HH:mm`) of a value, or an empty string when the
22
+ * value has no time.
23
+ */
24
+ function getTimePart(value, type) {
25
+ if (type === "time") return value;
26
+ const [, time = ""] = value.split("T");
27
+ return time;
28
+ }
29
+ /**
30
+ * Builds a value for the given type, or an empty string while any required
31
+ * part is missing.
32
+ */
33
+ function joinDateTime(type, date, time) {
34
+ if (type === "date") return date;
35
+ if (type === "time") return time;
36
+ return date && time ? `${date}T${time}` : "";
37
+ }
38
+ /**
39
+ * Formats a value to be displayed in pt-BR, like `24/09/2026`, `14:30` or
40
+ * `24/09/2026 14:30`.
41
+ */
42
+ function formatDateTime(value, type) {
43
+ return [getDatePart(value, type).split("-").reverse().join("/"), getTimePart(value, type)].filter(Boolean).join(" ");
44
+ }
45
+ function useDateTimeFieldRules(props) {
46
+ const defaultRules = useRules(props);
47
+ return computed(() => {
48
+ const rules = [...defaultRules.value];
49
+ if (props.min) rules.push(minRule(props.min, props.type));
50
+ if (props.max) rules.push(maxRule(props.max, props.type));
51
+ return rules;
52
+ });
53
+ }
54
+ /**
55
+ * Computes the limits of each picker. For `datetime`, time limits only apply
56
+ * when the selected date is the limit's own date.
57
+ */
58
+ function useDateTimeFieldPickerBounds(props, selectedDate) {
59
+ return computed(() => {
60
+ const min = props.min ?? "";
61
+ const max = props.max ?? "";
62
+ const date = toValue(selectedDate);
63
+ return {
64
+ dateMin: getDatePart(min, props.type),
65
+ dateMax: getDatePart(max, props.type),
66
+ timeMin: getTimeBound(min, props.type, date),
67
+ timeMax: getTimeBound(max, props.type, date)
68
+ };
69
+ });
70
+ }
71
+ /**
72
+ * Makes the field's input the activator of the pickers' menu, so the menu's attributes are on an
73
+ * element whose role accepts them.
74
+ * @param isOpen - Whether the menu is open.
75
+ * @param isDisabled - Whether the menu can't open, as when the field is disabled or readonly.
76
+ * @param hasLabel - Whether the field has a label, which names the menu's dialog.
77
+ */
78
+ function useDateTimeFieldMenu(isOpen, isDisabled, hasLabel) {
79
+ const inputId = useId();
80
+ const activator = computed(() => toValue(isDisabled) ? void 0 : `#${inputId}`);
81
+ const contentProps = computed(() => ({
82
+ role: "dialog",
83
+ "aria-labelledby": toValue(hasLabel) ? `${inputId}-label` : void 0
84
+ }));
85
+ function open() {
86
+ if (!toValue(isDisabled)) isOpen.value = true;
87
+ }
88
+ return {
89
+ inputId,
90
+ activator,
91
+ activatorProps,
92
+ contentProps,
93
+ open
94
+ };
95
+ }
96
+ function getTimeBound(bound, type, selectedDate) {
97
+ if (type === "datetime" && getDatePart(bound, type) !== selectedDate) return "";
98
+ return getTimePart(bound, type);
99
+ }
100
+ function minRule(min, type) {
101
+ const message = `Valor anterior ao mínimo permitido (${formatDateTime(min, type)})`;
102
+ return (value) => !value || value >= min || message;
103
+ }
104
+ function maxRule(max, type) {
105
+ const message = `Valor posterior ao máximo permitido (${formatDateTime(max, type)})`;
106
+ return (value) => !value || value <= max || message;
107
+ }
108
+ //#endregion
109
+ export { formatDateTime, getDatePart, getTimePart, joinDateTime, useDateTimeFieldMenu, useDateTimeFieldPickerBounds, useDateTimeFieldRules };
@@ -0,0 +1,28 @@
1
+ import { required } from "./rules.js";
2
+ import { computed, toValue } from "vue";
3
+ //#region src/composables/inputs/fields.ts
4
+ const textFieldVariantToVuetifyVariant = {
5
+ primary: "underlined",
6
+ secondary: "outlined"
7
+ };
8
+ function useTextFieldVariant(variant) {
9
+ return computed(() => {
10
+ const variantValue = toValue(variant);
11
+ return textFieldVariantToVuetifyVariant[variantValue];
12
+ });
13
+ }
14
+ function createRequiredRule(required, isPresent) {
15
+ return (value) => {
16
+ if (!required) return true;
17
+ return isPresent(value) || "Campo obrigatório";
18
+ };
19
+ }
20
+ function useRules(options) {
21
+ return computed(() => {
22
+ const rules = [];
23
+ if (options.required) rules.push(required);
24
+ return rules;
25
+ });
26
+ }
27
+ //#endregion
28
+ export { createRequiredRule, useRules, useTextFieldVariant };
@@ -0,0 +1,133 @@
1
+ import { useRules } from "./fields.js";
2
+ import { useMask } from "vuetify";
3
+ import { computed } from "vue";
4
+ //#region src/composables/inputs/mask-field.ts
5
+ /**
6
+ * Masks of the presets, as Vuetify reads them: `#` is a digit and `N` is a
7
+ * letter or a digit, with letters converted to uppercase. Our preset names
8
+ * always map to these masks, never to Vuetify's own presets (e.g. its American
9
+ * `phone`).
10
+ */
11
+ const maskFieldPresets = {
12
+ cpf: "###.###.###-##",
13
+ cnpj: "NN.NNN.NNN/NNNN-##",
14
+ cep: "#####-###",
15
+ phone: "(##) #####-####",
16
+ date: "##/##/####"
17
+ };
18
+ /**
19
+ * Returns the mask as Vuetify reads it: the mask of a preset, or the given mask
20
+ * itself, escaped, when it's a custom one.
21
+ *
22
+ * It takes any string, as the `mask` prop may get one through a cast or from
23
+ * JavaScript: a string that isn't a preset is taken as the mask.
24
+ */
25
+ function resolveMask(mask) {
26
+ return isMaskFieldPreset(mask) ? maskFieldPresets[mask] : toVuetifyMask(mask);
27
+ }
28
+ /**
29
+ * Returns a custom mask as Vuetify reads it. `#` is the only token of custom
30
+ * masks, but Vuetify always adds its own tokens (`A`, `a`, `N`, `n` and `X`)
31
+ * and its escape (`\`), so these characters are escaped to stay literals.
32
+ */
33
+ function toVuetifyMask(mask) {
34
+ return mask.replaceAll(/[AaNnX\\]/g, String.raw`\$&`);
35
+ }
36
+ /**
37
+ * Returns the tokens of a mask as Vuetify reads it (see {@link resolveMask}),
38
+ * in order, without its literals (e.g. `NNNNNNNNNNNN##` for `cnpj`).
39
+ */
40
+ function getMaskTokens(mask) {
41
+ return mask.replaceAll(/\\.|[^#N]/g, "");
42
+ }
43
+ /**
44
+ * Returns the default placeholder of a mask as Vuetify reads it (see
45
+ * {@link resolveMask}): the mask with `0` in place of each token, keeping its
46
+ * literals (e.g. `000.000.000-00` for `cpf` and `Nº 0000` for `Nº ####`).
47
+ */
48
+ function getMaskPlaceholder(mask) {
49
+ return mask.replaceAll(/\\(.)|[#N]/g, (_token, literal) => literal ?? "0");
50
+ }
51
+ function isMaskFieldPreset(mask) {
52
+ return Object.hasOwn(maskFieldPresets, mask);
53
+ }
54
+ /**
55
+ * Keeps the input masked while the user edits it, emitting only the
56
+ * characters of the tokens (Vuetify's `unmask`), with letters in uppercase.
57
+ *
58
+ * A `v-model` out of the mask's format (masked, with lowercase letters, or with
59
+ * characters or length the mask doesn't accept) is displayed normalized,
60
+ * without being emitted, and validated as it is, so a value that doesn't fit
61
+ * the mask fails validation.
62
+ */
63
+ function useMaskField(props, model) {
64
+ const mask = computed(() => resolveMask(props.mask));
65
+ const tokens = computed(() => getMaskTokens(mask.value));
66
+ const acceptsLetters = computed(() => tokens.value.includes("N"));
67
+ const vuetifyMask = useMask({ get mask() {
68
+ return {
69
+ mask: mask.value,
70
+ tokens: {}
71
+ };
72
+ } });
73
+ const displayValue = computed(() => vuetifyMask.mask(unmask(model.value)));
74
+ const placeholder = computed(() => props.placeholder ?? getMaskPlaceholder(mask.value));
75
+ const inputMode = computed(() => acceptsLetters.value ? "text" : "numeric");
76
+ const autocapitalize = computed(() => acceptsLetters.value ? "characters" : void 0);
77
+ const defaultRules = useRules(props);
78
+ const rules = computed(() => [
79
+ ...defaultRules.value,
80
+ (value) => !value || !isValid(value) || isComplete(value) || "Valor incompleto",
81
+ (value) => !value || isValid(value) || "Valor inválido"
82
+ ]);
83
+ function unmask(text) {
84
+ return vuetifyMask.unmask(text) ?? "";
85
+ }
86
+ function isValid(value) {
87
+ return unmask(value).toUpperCase() === unmask(vuetifyMask.mask(value));
88
+ }
89
+ function isComplete(value) {
90
+ return unmask(vuetifyMask.mask(value)).length === tokens.value.length;
91
+ }
92
+ /**
93
+ * Returns the position in a masked text right after the given number of
94
+ * characters of the value, skipping the literals after it unless the user
95
+ * deleted backwards (as Vuetify's mask input does).
96
+ */
97
+ function findCaret(masked, valueLength, deletedBackwards) {
98
+ let caret = 0;
99
+ let found = 0;
100
+ while (found < valueLength) {
101
+ if (!vuetifyMask.isDelimiter(masked, caret)) found++;
102
+ caret++;
103
+ }
104
+ if (!deletedBackwards) while (vuetifyMask.isDelimiter(masked, caret)) caret++;
105
+ return caret;
106
+ }
107
+ function update(input, deletedBackwards) {
108
+ const caret = input.selectionStart ?? input.value.length;
109
+ const masked = vuetifyMask.mask(unmask(input.value));
110
+ const newCaret = findCaret(masked, unmask(vuetifyMask.mask(unmask(input.value.slice(0, caret)))).length, deletedBackwards);
111
+ const value = unmask(masked);
112
+ input.value = masked;
113
+ input.setSelectionRange(newCaret, newCaret);
114
+ if (value !== unmask(displayValue.value)) model.value = value;
115
+ }
116
+ function onInput(event) {
117
+ if (!event.isComposing) update(event.target, event.inputType.endsWith("Backward"));
118
+ }
119
+ function onCompositionEnd(event) {
120
+ update(event.target, false);
121
+ }
122
+ return {
123
+ displayValue,
124
+ placeholder,
125
+ inputMode,
126
+ autocapitalize,
127
+ rules,
128
+ onInput,
129
+ onCompositionEnd
130
+ };
131
+ }
132
+ //#endregion
133
+ export { getMaskPlaceholder, getMaskTokens, maskFieldPresets, resolveMask, toVuetifyMask, useMaskField };
@@ -0,0 +1,16 @@
1
+ import { isEmail, isEmpty, isPhone, isUrl } from "@nexdom/shared/utils";
2
+ //#region src/composables/inputs/rules.ts
3
+ function required(value) {
4
+ return !isEmpty(value) || "Campo obrigatório";
5
+ }
6
+ function phone(value) {
7
+ return isEmpty(value) || isPhone(value) || "Número inválido";
8
+ }
9
+ function email(value) {
10
+ return isEmpty(value) || isEmail(value) || "E-mail inválido";
11
+ }
12
+ function url(value) {
13
+ return isEmpty(value) || isUrl(value) || "URL inválida";
14
+ }
15
+ //#endregion
16
+ export { email, phone, required, url };
@@ -0,0 +1,81 @@
1
+ import { icons } from "../../consts/icons.js";
2
+ import { email, phone, url } from "./rules.js";
3
+ import { useRules } from "./fields.js";
4
+ import { computed, readonly, ref, toValue, watch } from "vue";
5
+ //#region src/composables/inputs/text-field.ts
6
+ const textFieldTypeToVuetifyType = {
7
+ text: "text",
8
+ phone: "tel",
9
+ email: "email",
10
+ url: "url",
11
+ password: "password",
12
+ search: "text"
13
+ };
14
+ const textFieldTypeToVuetifyRule = {
15
+ phone,
16
+ email,
17
+ url
18
+ };
19
+ /**
20
+ * Maps the field type to the input type, showing as plain text a password
21
+ * the user chose to make visible.
22
+ */
23
+ function useTextFieldType(type, isPasswordVisible) {
24
+ return computed(() => {
25
+ const typeValue = toValue(type);
26
+ if (typeValue === "password" && toValue(isPasswordVisible)) return "text";
27
+ return textFieldTypeToVuetifyType[typeValue];
28
+ });
29
+ }
30
+ /**
31
+ * Controls the icon at the end of the field: a decorative magnifier on
32
+ * `search`, and the toggle that shows or hides the value on `password`. The
33
+ * toggle works with click, `Enter` and `Space`, does nothing while disabled,
34
+ * and the password hides again whenever the type stops being `password`.
35
+ */
36
+ function useTextFieldAppendIcon(type, disabled) {
37
+ const isPasswordField = computed(() => toValue(type) === "password");
38
+ const isPasswordVisible = ref(false);
39
+ watch(isPasswordField, () => {
40
+ isPasswordVisible.value = false;
41
+ });
42
+ function toggle() {
43
+ if (toValue(disabled)) return;
44
+ isPasswordVisible.value = !isPasswordVisible.value;
45
+ }
46
+ function onKeydown(event) {
47
+ if (event.key !== "Enter" && event.key !== " ") return;
48
+ event.preventDefault();
49
+ event.stopPropagation();
50
+ toggle();
51
+ }
52
+ const iconProps = computed(() => {
53
+ if (toValue(type) === "search") return { icon: icons.search };
54
+ if (!isPasswordField.value) return void 0;
55
+ return {
56
+ icon: isPasswordVisible.value ? icons.hidePassword : icons.showPassword,
57
+ "aria-label": "Mostrar senha",
58
+ "aria-pressed": isPasswordVisible.value,
59
+ onClick: toggle,
60
+ onKeydown
61
+ };
62
+ });
63
+ return {
64
+ isPasswordVisible: readonly(isPasswordVisible),
65
+ iconProps
66
+ };
67
+ }
68
+ function useTextFieldRules(props) {
69
+ const defaultRules = useRules(props);
70
+ return computed(() => {
71
+ const rules = [...defaultRules.value];
72
+ const typeRule = associatedRuleType(props.type) ? textFieldTypeToVuetifyRule[props.type] : void 0;
73
+ if (typeRule) rules.push(typeRule);
74
+ return rules;
75
+ });
76
+ }
77
+ function associatedRuleType(type) {
78
+ return type in textFieldTypeToVuetifyRule;
79
+ }
80
+ //#endregion
81
+ export { useTextFieldAppendIcon, useTextFieldRules, useTextFieldType };