@nexdom/uimed-vue 2.0.0-beta.4 → 2.0.0-beta.40

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.
@@ -1,4 +1,5 @@
1
- import { n as FeedbackColorVariant } from "./types-BPXXtbmW.js";
1
+ import { i as FeedbackColorVariant, n as ButtonVariant, r as ColorVariant } from "./types-CjaJ2WAC.js";
2
+ import { ComputedRef, MaybeRefOrGetter } from "vue";
2
3
  //#region src/composables/dialogs/types.d.ts
3
4
  interface ToastOptions {
4
5
  /**
@@ -6,12 +7,84 @@ interface ToastOptions {
6
7
  */
7
8
  message: string;
8
9
  /**
9
- * Applies a color to the button.
10
+ * Applies a color to the toast.
10
11
  * One of `positive`, `informative`, `caution`, or `danger`.
11
12
  * @default "informative"
12
13
  */
13
14
  color?: FeedbackColorVariant;
14
15
  }
16
+ /**
17
+ * A button displayed at the bottom of a dialog opened by `useDialog`'s `dialog`.
18
+ */
19
+ interface DialogAction<T extends string = string> {
20
+ /**
21
+ * Text displayed on the button.
22
+ */
23
+ text: string;
24
+ /**
25
+ * Value the dialog resolves with when the button is clicked.
26
+ */
27
+ value: T;
28
+ /**
29
+ * Applies a distinct style variation to the button.
30
+ * One of `primary`, `secondary`, or `ghost`.
31
+ * @default "ghost", or "primary" for the last action
32
+ */
33
+ variant?: ButtonVariant;
34
+ /**
35
+ * Applies a color to the button.
36
+ * One of `primary`, `secondary`, `positive`, `informative`, `caution`, or `danger`.
37
+ * @default "primary"
38
+ */
39
+ color?: ColorVariant;
40
+ }
41
+ /**
42
+ * Options of a dialog opened by `useDialog`'s `dialog`.
43
+ */
44
+ interface DialogOptions<T extends string = string> {
45
+ /**
46
+ * Plain text displayed as the dialog's title. HTML isn't interpreted.
47
+ */
48
+ title?: string;
49
+ /**
50
+ * Plain text displayed as the dialog's message. HTML isn't interpreted.
51
+ */
52
+ message: string;
53
+ /**
54
+ * Buttons displayed at the bottom of the dialog, right-aligned, in the given order.
55
+ * @default [{ text: "Fechar" }], which resolves `undefined`
56
+ */
57
+ actions?: DialogAction<T>[];
58
+ }
59
+ /**
60
+ * Options of a dialog opened by `useConfirm`'s `confirm`.
61
+ */
62
+ interface ConfirmOptions {
63
+ /**
64
+ * Plain text displayed as the dialog's title. HTML isn't interpreted.
65
+ */
66
+ title?: string;
67
+ /**
68
+ * Plain text displayed as the dialog's message. HTML isn't interpreted.
69
+ */
70
+ message: string;
71
+ /**
72
+ * Text displayed on the confirm button.
73
+ * @default "Confirmar"
74
+ */
75
+ confirmText?: string;
76
+ /**
77
+ * Text displayed on the cancel button.
78
+ * @default "Cancelar"
79
+ */
80
+ cancelText?: string;
81
+ /**
82
+ * Applies a color to the confirm button.
83
+ * One of `primary`, `secondary`, `positive`, `informative`, `caution`, or `danger`.
84
+ * @default "primary"
85
+ */
86
+ color?: ColorVariant;
87
+ }
15
88
  //#endregion
16
89
  //#region src/composables/dialogs/use-toast.d.ts
17
90
  /**
@@ -37,7 +110,7 @@ declare function useToast(): {
37
110
  */
38
111
  declare function toast(options: ToastOptions): void;
39
112
  //#endregion
40
- //#region src/composables/dialogs/use-run-or-toast..d.ts
113
+ //#region src/composables/dialogs/use-run-or-toast.d.ts
41
114
  /**
42
115
  * Generate `run` method and a computed property `isRunning`.
43
116
  * Calling `run` will reactively update `isRunning`.
@@ -64,4 +137,143 @@ declare function useRunOrToast(): {
64
137
  isRunning: import("vue").ComputedRef<boolean>;
65
138
  };
66
139
  //#endregion
67
- export { useRunOrToast, useToast };
140
+ //#region src/composables/dialogs/use-dialog.d.ts
141
+ /**
142
+ * Composable which returns a function to display dialogs.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * const { dialog } = useDialog();
147
+ *
148
+ * const answer = await dialog({
149
+ * title: "Sessão expirando",
150
+ * message: "Deseja continuar conectado?",
151
+ * actions: [
152
+ * { text: "Sair", value: "leave" },
153
+ * { text: "Continuar", value: "stay" },
154
+ * ],
155
+ * });
156
+ * ```
157
+ */
158
+ declare function useDialog(): {
159
+ dialog: typeof dialog;
160
+ };
161
+ /**
162
+ * Displays a dialog. Dialogs are displayed one at a time, in the order they were requested.
163
+ * @param {DialogOptions} options - Options for the dialog.
164
+ * @returns the `value` of the clicked action, or `undefined` when the dialog is closed by the
165
+ * default action, the `Esc` key, a click outside it or the browser's back button.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * await dialog({ title: "Aviso", message: "Seu cadastro foi enviado para análise." });
170
+ * ```
171
+ */
172
+ declare function dialog<T extends string = never>(options: DialogOptions<T>): Promise<T | undefined>;
173
+ //#endregion
174
+ //#region src/composables/dialogs/use-confirm.d.ts
175
+ /**
176
+ * Composable which returns a function to run actions only after the user confirms them in a
177
+ * dialog, and a computed property `isRunning`.
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * const { confirm, isRunning } = useConfirm();
182
+ *
183
+ * const removed = await confirm(
184
+ * { title: "Excluir paciente", message: "Esta ação não pode ser desfeita.", confirmText: "Excluir", color: "danger" },
185
+ * () => api.deletePatient(id),
186
+ * );
187
+ * ```
188
+ */
189
+ declare function useConfirm(): {
190
+ confirm: <F extends (...args: Parameters<F>) => PromiseLike<Awaited<ReturnType<F>>>>(options: ConfirmOptions, action: F, ...params: Parameters<F>) => Promise<Awaited<ReturnType<F>> | false>;
191
+ isRunning: import("vue").ComputedRef<boolean>;
192
+ };
193
+ //#endregion
194
+ //#region src/composables/navigation/types.d.ts
195
+ /**
196
+ * Values compared by `useUnsavedChanges`.
197
+ */
198
+ interface UnsavedChangesValues<T> {
199
+ /**
200
+ * Original (last saved) value. To mark the form as saved, assign it a copy of the current value.
201
+ */
202
+ previous: MaybeRefOrGetter<T>;
203
+ /**
204
+ * Current value of the form.
205
+ */
206
+ current: MaybeRefOrGetter<T>;
207
+ }
208
+ /**
209
+ * Options of `useUnsavedChanges`.
210
+ */
211
+ interface UnsavedChangesOptions {
212
+ /**
213
+ * Plain text displayed as the confirmation dialog's title. HTML isn't interpreted.
214
+ * @default "Alterações não salvas"
215
+ */
216
+ title?: string;
217
+ /**
218
+ * Plain text displayed as the confirmation dialog's message. HTML isn't interpreted.
219
+ * @default "Existem alterações que ainda não foram salvas. Deseja sair sem salvar?"
220
+ */
221
+ message?: string;
222
+ /**
223
+ * Text displayed on the button which leaves the page.
224
+ * @default "Sair sem salvar"
225
+ */
226
+ confirmText?: string;
227
+ /**
228
+ * Text displayed on the button which cancels the navigation.
229
+ * @default "Continuar editando"
230
+ */
231
+ cancelText?: string;
232
+ /**
233
+ * Applies a color to the button which leaves the page.
234
+ * One of `primary`, `secondary`, `positive`, `informative`, `caution`, or `danger`.
235
+ * @default "danger"
236
+ */
237
+ color?: ColorVariant;
238
+ /**
239
+ * Also asks for the browser's native confirmation on reload (F5), tab close or leaving the app
240
+ * through an external link.
241
+ * @default true
242
+ */
243
+ beforeUnload?: boolean;
244
+ }
245
+ //#endregion
246
+ //#region src/composables/navigation/use-unsaved-changes.d.ts
247
+ /**
248
+ * Composable which compares the original and the current values of a form, and asks the user to
249
+ * confirm before leaving the page while they differ.
250
+ *
251
+ * Must be called in the `setup` of a component. Leaving through vue-router is only guarded in
252
+ * components rendered by a `<RouterView>`, and the confirmation dialog is displayed by `UMain`.
253
+ *
254
+ * @param values - The original (`previous`) and current (`current`) values, as refs, getters or
255
+ * reactive objects. They're compared in depth: key order doesn't matter, a key holding `undefined`
256
+ * equals a missing key, dates are compared by their time and arrays item by item, in order.
257
+ * @param options - Texts and color of the confirmation dialog, and whether to ask for the
258
+ * browser's native confirmation before unloading the page.
259
+ * @returns `hasChanges`, a computed property which is `true` while the values differ
260
+ *
261
+ * @example
262
+ * ```ts
263
+ * const saved = ref({ name: "Maria" });
264
+ * const form = ref(structuredClone(toRaw(saved.value)));
265
+ *
266
+ * const { hasChanges } = useUnsavedChanges({ previous: saved, current: form });
267
+ *
268
+ * async function save() {
269
+ * await api.save(form.value);
270
+ * // Marks the form as saved
271
+ * saved.value = structuredClone(toRaw(form.value));
272
+ * }
273
+ * ```
274
+ */
275
+ declare function useUnsavedChanges<T>(values: UnsavedChangesValues<T>, options?: UnsavedChangesOptions): {
276
+ hasChanges: ComputedRef<boolean>;
277
+ };
278
+ //#endregion
279
+ export { useConfirm, useDialog, useRunOrToast, useToast, useUnsavedChanges };
@@ -1,6 +1,8 @@
1
- import { n as useToast } from "./use-toast-CACuYhK_.js";
2
- import { computed, ref } from "vue";
3
- //#region src/composables/dialogs/use-run-or-toast..ts
1
+ import { i as useDialog, o as useToast, t as openDialog } from "./use-dialog-Ck2_ZW7T.js";
2
+ import { computed, onActivated, onDeactivated, onMounted, onUnmounted, ref, toValue, watch } from "vue";
3
+ import { isDeepEqual } from "@nexdom/shared/utils";
4
+ import { onBeforeRouteLeave } from "vue-router";
5
+ //#region src/composables/dialogs/use-run-or-toast.ts
4
6
  const { toast } = useToast();
5
7
  /**
6
8
  * Generate `run` method and a computed property `isRunning`.
@@ -62,4 +64,139 @@ function useRunOrToast() {
62
64
  };
63
65
  }
64
66
  //#endregion
65
- export { useRunOrToast, useToast };
67
+ //#region src/composables/dialogs/use-confirm.ts
68
+ /**
69
+ * Composable which returns a function to run actions only after the user confirms them in a
70
+ * dialog, and a computed property `isRunning`.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const { confirm, isRunning } = useConfirm();
75
+ *
76
+ * const removed = await confirm(
77
+ * { title: "Excluir paciente", message: "Esta ação não pode ser desfeita.", confirmText: "Excluir", color: "danger" },
78
+ * () => api.deletePatient(id),
79
+ * );
80
+ * ```
81
+ */
82
+ function useConfirm() {
83
+ const { run, isRunning } = useRunOrToast();
84
+ /**
85
+ * Displays a confirmation dialog and, if the user confirms, runs the given function while the
86
+ * dialog shows the confirm button loading. If the function throws an error, the dialog closes
87
+ * and a toast shows the error message.
88
+ * @returns the function returned value, or `false` if the user cancels or the function fails
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * const { confirm } = useConfirm();
93
+ *
94
+ * // Call like this:
95
+ * const result1 = await confirm({ message: "Salvar alterações?" }, () => save(form));
96
+ *
97
+ * // Or this:
98
+ * const result2 = await confirm({ message: "Salvar alterações?" }, save, form);
99
+ * ```
100
+ */
101
+ function confirm(options, action, ...params) {
102
+ return openDialog({
103
+ title: options.title,
104
+ message: options.message,
105
+ role: "alertdialog",
106
+ actions: [{ text: options.cancelText ?? "Cancelar" }, {
107
+ text: options.confirmText ?? "Confirmar",
108
+ value: "confirm",
109
+ color: options.color
110
+ }]
111
+ }, (value) => value === "confirm" ? run(action, ...params) : false);
112
+ }
113
+ return {
114
+ confirm,
115
+ isRunning
116
+ };
117
+ }
118
+ //#endregion
119
+ //#region src/composables/navigation/use-unsaved-changes.ts
120
+ /**
121
+ * Calls whose page is on display with unsaved changes. The `beforeunload` listener is registered
122
+ * while at least one of them exists, so a call that stops guarding doesn't remove the guard of the
123
+ * others.
124
+ */
125
+ const unloadGuards = /* @__PURE__ */ new Set();
126
+ function preventUnload(event) {
127
+ event.preventDefault();
128
+ }
129
+ function updateUnloadListener() {
130
+ if (unloadGuards.size > 0) globalThis.addEventListener("beforeunload", preventUnload);
131
+ else globalThis.removeEventListener("beforeunload", preventUnload);
132
+ }
133
+ /**
134
+ * Composable which compares the original and the current values of a form, and asks the user to
135
+ * confirm before leaving the page while they differ.
136
+ *
137
+ * Must be called in the `setup` of a component. Leaving through vue-router is only guarded in
138
+ * components rendered by a `<RouterView>`, and the confirmation dialog is displayed by `UMain`.
139
+ *
140
+ * @param values - The original (`previous`) and current (`current`) values, as refs, getters or
141
+ * reactive objects. They're compared in depth: key order doesn't matter, a key holding `undefined`
142
+ * equals a missing key, dates are compared by their time and arrays item by item, in order.
143
+ * @param options - Texts and color of the confirmation dialog, and whether to ask for the
144
+ * browser's native confirmation before unloading the page.
145
+ * @returns `hasChanges`, a computed property which is `true` while the values differ
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * const saved = ref({ name: "Maria" });
150
+ * const form = ref(structuredClone(toRaw(saved.value)));
151
+ *
152
+ * const { hasChanges } = useUnsavedChanges({ previous: saved, current: form });
153
+ *
154
+ * async function save() {
155
+ * await api.save(form.value);
156
+ * // Marks the form as saved
157
+ * saved.value = structuredClone(toRaw(form.value));
158
+ * }
159
+ * ```
160
+ */
161
+ function useUnsavedChanges(values, options = {}) {
162
+ const hasChanges = computed(() => !isDeepEqual(toValue(values.previous), toValue(values.current)));
163
+ const { confirm } = useConfirm();
164
+ /** Answer of the dialog on display, shared by every navigation made while it's open. */
165
+ let pending;
166
+ onBeforeRouteLeave(() => {
167
+ if (!hasChanges.value) return true;
168
+ pending ??= confirm({
169
+ title: options.title ?? "Alterações não salvas",
170
+ message: options.message ?? "Existem alterações que ainda não foram salvas. Deseja sair sem salvar?",
171
+ confirmText: options.confirmText ?? "Sair sem salvar",
172
+ cancelText: options.cancelText ?? "Continuar editando",
173
+ color: options.color ?? "danger"
174
+ }, async () => {}).then((answer) => answer !== false).finally(() => pending = void 0);
175
+ return pending;
176
+ });
177
+ /** Whether the component is on display, which is false while `<KeepAlive>` keeps it deactivated. */
178
+ let isActive = true;
179
+ if (options.beforeUnload ?? true) {
180
+ onMounted(() => watch(hasChanges, updateBeforeUnload, { immediate: true }));
181
+ onUnmounted(deactivate);
182
+ onDeactivated(deactivate);
183
+ onActivated(() => {
184
+ isActive = true;
185
+ updateBeforeUnload();
186
+ });
187
+ }
188
+ /** Identifies this call in `unloadGuards`. */
189
+ const unloadGuard = {};
190
+ function updateBeforeUnload() {
191
+ if (hasChanges.value && isActive) unloadGuards.add(unloadGuard);
192
+ else unloadGuards.delete(unloadGuard);
193
+ updateUnloadListener();
194
+ }
195
+ function deactivate() {
196
+ isActive = false;
197
+ updateBeforeUnload();
198
+ }
199
+ return { hasChanges };
200
+ }
201
+ //#endregion
202
+ export { useConfirm, useDialog, useRunOrToast, useToast, useUnsavedChanges };
package/dist/index.d.ts CHANGED
@@ -1,23 +1,11 @@
1
- import "@fontsource/roboto/100.css";
2
- import "@fontsource/roboto/300.css";
3
- import "@fontsource/roboto/400.css";
4
- import "@fontsource/roboto/500.css";
5
- import "@fontsource/roboto/700.css";
6
- import "@fontsource/roboto/900.css";
7
- import "@fontsource/roboto/100-italic.css";
8
- import "@fontsource/roboto/300-italic.css";
9
- import "@fontsource/roboto/400-italic.css";
10
- import "@fontsource/roboto/500-italic.css";
11
- import "@fontsource/roboto/700-italic.css";
12
- import "@fontsource/roboto/900-italic.css";
13
1
  import "@mdi/font/css/materialdesignicons.css";
14
2
  import "vuetify/styles";
3
+ import "./styles/fonts/fonts.css";
15
4
  import { Plugin } from "vue";
16
5
  //#region src/index.d.ts
17
6
  /**
18
7
  * Create an UIMed-Vue instance to be installed after [createApp](https://vuejs.org/guide/essentials/application.html#the-application-instance).
19
8
  * @returns an instance to be used with [app.use](https://vuejs.org/guide/essentials/application.html#the-application-instance)
20
9
  */
21
- declare function createUimed(): Plugin;
22
- //#endregion
23
- export { createUimed };
10
+ export declare function createUimed(): Plugin;
11
+ //#endregion
package/dist/index.js CHANGED
@@ -1,17 +1,7 @@
1
- import "@fontsource/roboto/100.css";
2
- import "@fontsource/roboto/300.css";
3
- import "@fontsource/roboto/400.css";
4
- import "@fontsource/roboto/500.css";
5
- import "@fontsource/roboto/700.css";
6
- import "@fontsource/roboto/900.css";
7
- import "@fontsource/roboto/100-italic.css";
8
- import "@fontsource/roboto/300-italic.css";
9
- import "@fontsource/roboto/400-italic.css";
10
- import "@fontsource/roboto/500-italic.css";
11
- import "@fontsource/roboto/700-italic.css";
12
- import "@fontsource/roboto/900-italic.css";
1
+ import { t as localeOptions } from "./locale-CM4o8cD_.js";
13
2
  import "@mdi/font/css/materialdesignicons.css";
14
3
  import "vuetify/styles";
4
+ import "./styles/fonts/fonts.css";
15
5
  import { createVuetify } from "vuetify";
16
6
  import "vue";
17
7
  //#region src/index.ts
@@ -20,7 +10,26 @@ import "vue";
20
10
  * @returns an instance to be used with [app.use](https://vuejs.org/guide/essentials/application.html#the-application-instance)
21
11
  */
22
12
  function createUimed() {
23
- return createVuetify({ theme: { defaultTheme: "light" } });
13
+ return createVuetify({
14
+ locale: localeOptions,
15
+ theme: {
16
+ defaultTheme: "light",
17
+ themes: { light: {
18
+ dark: false,
19
+ colors: {
20
+ background: "#EAEDF0",
21
+ surface: "#FCFCFD",
22
+ primary: "#00995D",
23
+ secondary: "#FCFCFD",
24
+ success: "#A9EFC5",
25
+ warning: "#FFE596",
26
+ error: "#D92D20",
27
+ info: "#9ACBE5"
28
+ },
29
+ variables: { "border-color": "#D0D5DD" }
30
+ } }
31
+ }
32
+ });
24
33
  }
25
34
  //#endregion
26
35
  export { createUimed };
@@ -0,0 +1,12 @@
1
+ import { pt } from "vuetify/locale";
2
+ //#region src/consts/locale.ts
3
+ /**
4
+ * Language of the texts the components generate on their own, such as
5
+ * screen reader announcements and pagination labels. Fixed in pt-BR.
6
+ */
7
+ const localeOptions = {
8
+ locale: "pt-BR",
9
+ messages: { "pt-BR": pt }
10
+ };
11
+ //#endregion
12
+ export { localeOptions as t };
package/dist/plugins.d.ts CHANGED
@@ -1,13 +1,24 @@
1
- import vitePluginVuetify from "vite-plugin-vuetify";
2
- import { UserConfig } from "vite-plus";
1
+ import { Plugin, UserConfig } from "vite-plus";
3
2
  //#region src/plugins.d.ts
4
3
  /**
5
- * Vite plugin for UIMed.
4
+ * Vite plugin that compiles the components' styles with the library's font and Sass settings, and
5
+ * loads the icon font with `font-display: swap`, so it doesn't block rendering while it loads.
6
+ * Required: without it, the components lose the Unimed Slab font. Needs `sass-embedded`, a peer
7
+ * dependency.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * // vite.config.ts
12
+ * import { vitePluginUimed } from "@nexdom/uimed-vue/plugins";
13
+ *
14
+ * export default defineConfig({
15
+ * plugins: [vue(), vitePluginUimed()],
16
+ * });
17
+ * ```
6
18
  */
7
- declare function vitePluginUimed(): ReturnType<typeof vitePluginVuetify>;
19
+ export declare function vitePluginUimed(): Plugin[];
8
20
  /**
9
21
  * Vitest Server plugin for UIMed.
10
22
  */
11
- declare function vitestServerPluginUimed(): NonNullable<NonNullable<UserConfig["test"]>["server"]>;
12
- //#endregion
13
- export { vitePluginUimed, vitestServerPluginUimed };
23
+ export declare function vitestServerPluginUimed(): NonNullable<NonNullable<UserConfig["test"]>["server"]>;
24
+ //#endregion
package/dist/plugins.js CHANGED
@@ -1,16 +1,84 @@
1
- import vitePluginVuetify from "vite-plugin-vuetify";
1
+ import { existsSync } from "node:fs";
2
+ import { pathToFileURL } from "node:url";
2
3
  //#region src/plugins.ts
4
+ /** Matches the ids of CSS files, with or without a query */
5
+ const stylesheet = /\.css(?:\?|$)/;
6
+ /** Path of Vuetify's stylesheets folder, inside the `node_modules` folder where it's installed */
7
+ const vuetifyStylesheets = "/node_modules/vuetify/lib/";
3
8
  /**
4
- * Vite plugin for UIMed.
9
+ * Matches the queries that import a stylesheet's file as is (`?raw`) or its URL (`?url`), which
10
+ * Vite doesn't compile. The URL still serves the compiled stylesheet, which Vite loads separately
11
+ */
12
+ const uncompiledStylesheetQuery = /[?&](?:raw|url)\b/;
13
+ /** Matches the ids of the icon font's stylesheet, with or without a query and Windows separators */
14
+ const iconFontStylesheet = /[\\/]node_modules[\\/]@mdi[\\/]font[\\/]css[\\/]materialdesignicons\.css(?:\?|$)/;
15
+ /** Start of the icon font's `@font-face` rule, the only one in its stylesheet */
16
+ const iconFontFace = "@font-face {";
17
+ /**
18
+ * Vite plugin that compiles the components' styles with the library's font and Sass settings, and
19
+ * loads the icon font with `font-display: swap`, so it doesn't block rendering while it loads.
20
+ * Required: without it, the components lose the Unimed Slab font. Needs `sass-embedded`, a peer
21
+ * dependency.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * // vite.config.ts
26
+ * import { vitePluginUimed } from "@nexdom/uimed-vue/plugins";
27
+ *
28
+ * export default defineConfig({
29
+ * plugins: [vue(), vitePluginUimed()],
30
+ * });
31
+ * ```
5
32
  */
6
33
  function vitePluginUimed() {
7
- return vitePluginVuetify({ autoImport: false });
34
+ const settings = new URL("./styles/settings.scss", import.meta.url);
35
+ let compiler;
36
+ return [{
37
+ name: "uimed:vuetify-styles",
38
+ enforce: "pre",
39
+ load: {
40
+ filter: { id: {
41
+ include: stylesheet,
42
+ exclude: uncompiledStylesheetQuery
43
+ } },
44
+ async handler(id) {
45
+ const file = id.split("?")[0].replaceAll("\\", "/");
46
+ const folder = file.lastIndexOf(vuetifyStylesheets);
47
+ if (folder === -1) return null;
48
+ const nodeModules = `${file.slice(0, folder)}/node_modules`;
49
+ const source = [".sass", ".scss"].map((extension) => `${file.slice(0, -4)}${extension}`).find((path) => existsSync(path));
50
+ if (!source) return null;
51
+ compiler ??= import("sass-embedded").then((sass) => sass.initAsyncCompiler());
52
+ const sass = await compiler;
53
+ const entry = `@use "${settings.href}";\n@use "${pathToFileURL(source).href}";`;
54
+ const { css } = await sass.compileStringAsync(entry, { loadPaths: [nodeModules] });
55
+ return css;
56
+ }
57
+ },
58
+ async closeBundle() {
59
+ const current = compiler;
60
+ compiler = void 0;
61
+ await (await current)?.dispose();
62
+ }
63
+ }, {
64
+ name: "uimed:icon-font",
65
+ enforce: "pre",
66
+ transform: {
67
+ filter: { id: {
68
+ include: iconFontStylesheet,
69
+ exclude: uncompiledStylesheetQuery
70
+ } },
71
+ handler(code) {
72
+ return code.replace(iconFontFace, `${iconFontFace} font-display: swap;`);
73
+ }
74
+ }
75
+ }];
8
76
  }
9
77
  /**
10
78
  * Vitest Server plugin for UIMed.
11
79
  */
12
80
  function vitestServerPluginUimed() {
13
- return { deps: { inline: ["vuetify"] } };
81
+ return { deps: { inline: ["vuetify", "@nexdom/uimed-vue"] } };
14
82
  }
15
83
  //#endregion
16
84
  export { vitePluginUimed, vitestServerPluginUimed };
@@ -0,0 +1,5 @@
1
+ @font-face {
2
+ font-family: "Unimed Slab";
3
+ src: url("./UnimedSlab-Regular.woff2");
4
+ font-display: swap;
5
+ }
@@ -0,0 +1,6 @@
1
+ @use "vuetify/settings" with (
2
+ $body-font-family: var(--v-font-body, "Unimed Slab", sans-serif),
3
+ // Unimed Slab's ascent and descent take 1.26em, more than the subtitles' default 1rem line
4
+ // height, which clipped its descenders and accented capitals
5
+ $list-item-subtitle-line-height: normal
6
+ );
@@ -0,0 +1,60 @@
1
+ //#region src/composables/colors/types.d.ts
2
+ type ColorVariant = "primary" | "secondary" | FeedbackColorVariant;
3
+ type FeedbackColorVariant = "positive" | "informative" | "caution" | "danger";
4
+ //#endregion
5
+ //#region src/components/button/types.d.ts
6
+ type ButtonVariant = "primary" | "secondary" | "ghost";
7
+ /**
8
+ * Props exposed by the {@link Button} component.
9
+ */
10
+ type ButtonProps = {
11
+ /**
12
+ * Applies a distinct style variation to the button.
13
+ * One of `primary`, `secondary`, or `ghost`.
14
+ * @default "primary"
15
+ */
16
+ variant?: ButtonVariant;
17
+ /**
18
+ * Applies a distinct behavior to the button.
19
+ * One of `button` or `submit`.
20
+ * @default "button"
21
+ */
22
+ type?: "submit" | "button";
23
+ /**
24
+ * Applies a color to the button.
25
+ * One of `primary`, `secondary`, `positive`, `informative`, `caution`, or `danger`.
26
+ * @default "primary"
27
+ */
28
+ color?: ColorVariant;
29
+ /**
30
+ * Removes the ability to click or target the button.
31
+ * @default false
32
+ */
33
+ disabled?: boolean;
34
+ /**
35
+ * Displays a loading indicator on the button and disables it while active, as `disabled` does:
36
+ * it can't be clicked or focused, `click` isn't emitted and its form isn't submitted, including
37
+ * by pressing `Enter` in one of the form's fields when it's the form's first submit button. It
38
+ * looks disabled, with the indicator, and loses focus if focused when loading starts, as any
39
+ * disabled button does. The button keeps its text as accessible name and is marked as busy, and
40
+ * assistive technologies ignore the indicator.
41
+ * @default false
42
+ */
43
+ loading?: boolean;
44
+ /**
45
+ * Stretches the button to the whole width of its parent, e.g. the only action of a form on a
46
+ * small screen.
47
+ * @default false
48
+ */
49
+ fullWidth?: boolean;
50
+ /**
51
+ * Associated [form](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/form) id.
52
+ */
53
+ form?: string;
54
+ /**
55
+ * Component id to use on automated tests.
56
+ */
57
+ dataTestid?: string;
58
+ };
59
+ //#endregion
60
+ export { FeedbackColorVariant as i, ButtonVariant as n, ColorVariant as r, ButtonProps as t };