@nexdom/uimed-vue 2.0.0-beta.5 → 2.0.0-beta.50
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 +126 -16
- package/README.md +1 -1
- package/dist/components.d.ts +1122 -98
- package/dist/components.js +2486 -319
- package/dist/composables.d.ts +216 -4
- package/dist/composables.js +141 -4
- package/dist/index.d.ts +3 -15
- package/dist/index.js +31 -13
- package/dist/locale-CM4o8cD_.js +12 -0
- package/dist/plugins.d.ts +18 -7
- package/dist/plugins.js +71 -3
- package/dist/styles/fonts/UnimedSlab-Regular.woff2 +0 -0
- package/dist/styles/fonts/fonts.css +5 -0
- package/dist/styles/settings.scss +6 -0
- package/dist/types-BbWo6RIy.d.ts +69 -0
- package/dist/unit-test.d.ts +2 -3
- package/dist/unit-test.js +3 -1
- package/dist/use-dialog-C0sM_bA2.js +138 -0
- package/package.json +9 -10
- package/dist/types-BPXXtbmW.d.ts +0 -5
- package/dist/use-toast-CACuYhK_.js +0 -53
package/dist/components.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { r as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { a as messages, c as colorToVuetifyColor, l as colorToVuetifyTextColor, n as owners$1, r as requests, s as useVuetifyColor } from "./use-dialog-C0sM_bA2.js";
|
|
2
|
+
import { useLocale, useMask } from "vuetify";
|
|
3
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, guardReactiveProps, inject, isRef, mergeModels, mergeProps, nextTick, normalizeClass, normalizeProps, onMounted, onScopeDispose, onUnmounted, openBlock, provide, readonly, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toValue, unref, useId, useModel, useSlots, useTemplateRef, watch, withCtx, withKeys, withModifiers } from "vue";
|
|
4
|
+
import { pt } from "vuetify/locale";
|
|
5
|
+
import { VApp, VAppBar, VAppBarNavIcon, VAppBarTitle, VAutocomplete, VAvatar, VBadge, VBtn, VCard, VCardActions, VCardText, VCheckboxBtn, VCol, VCombobox, VContainer, VDataIterator, VDatePicker, VDialog, VDivider, VEmptyState, VExpansionPanel, VExpansionPanelText, VExpansionPanelTitle, VExpansionPanels, VFooter, VForm, VIcon, VIconBtn, VImg, VList, VListGroup, VListItem, VListItemAction, VListSubheader, VLocaleProvider, VMain, VMenu, VNavigationDrawer, VPagination, VProgressCircular, VProgressLinear, VRow, VSkeletonLoader, VSnackbarQueue, VTable, VTextField, VTimePicker } from "vuetify/components";
|
|
6
|
+
import { formatDateTime, isEmail, isEmpty, isPhone, isUrl, toInitials } from "@nexdom/shared/utils";
|
|
7
|
+
import { VHighlight } from "vuetify/labs/VHighlight";
|
|
8
|
+
import { RouterLink } from "vue-router";
|
|
5
9
|
//#region src/composables/navigation/use-route-props.ts
|
|
6
10
|
function useRouteProps(route) {
|
|
7
11
|
return computed(() => routeToProps(route));
|
|
@@ -10,12 +14,187 @@ function routeToProps(route) {
|
|
|
10
14
|
const location = toValue(route);
|
|
11
15
|
return isExternalRoute(location) ? { href: location } : { to: location };
|
|
12
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Maps a menu item's route to its list item's props. An item without a route only runs its action,
|
|
19
|
+
* so it's a button: Vuetify would make it a list item, which the menus' groups of links don't allow.
|
|
20
|
+
*/
|
|
21
|
+
function routeToListItemProps(route) {
|
|
22
|
+
return route ? routeToProps(route) : { role: "button" };
|
|
23
|
+
}
|
|
13
24
|
function isExternalRoute(route) {
|
|
14
25
|
return typeof route === "string" && route.startsWith("http");
|
|
15
26
|
}
|
|
16
27
|
//#endregion
|
|
28
|
+
//#region src/composables/navigation/use-navigation-search-shortcut.ts
|
|
29
|
+
/**
|
|
30
|
+
* Mounted navigation menus, in mounting order. Only the first one answers the shortcut, so pages
|
|
31
|
+
* with more than one `UMain` (like the docs) don't open every menu at once.
|
|
32
|
+
*/
|
|
33
|
+
const owners = ref([]);
|
|
34
|
+
/**
|
|
35
|
+
* Focus targets where the shortcut is left to the page: overlays (dialogs, confirmations and
|
|
36
|
+
* menus) and other modals, the activators of open menus (which keep the focus when opened by a
|
|
37
|
+
* click, and would pull it back from the navigation menu) and rich text editors, which usually
|
|
38
|
+
* take Ctrl+K to insert links.
|
|
39
|
+
*/
|
|
40
|
+
const IGNORED_TARGETS = [
|
|
41
|
+
".v-overlay",
|
|
42
|
+
"dialog",
|
|
43
|
+
"[aria-modal='true']",
|
|
44
|
+
"[aria-haspopup][aria-expanded='true']",
|
|
45
|
+
"[contenteditable]:not([contenteditable='false'])"
|
|
46
|
+
].join(", ");
|
|
47
|
+
/** Accessible name of the search field, whose placeholder also shows the shortcut. */
|
|
48
|
+
const SEARCH_NAME = "Buscar";
|
|
49
|
+
/**
|
|
50
|
+
* Shortcut keys for the current platform. Starts with the non-macOS keys and switches on mount,
|
|
51
|
+
* so server-rendered pages (the docs) hydrate without mismatches.
|
|
52
|
+
*/
|
|
53
|
+
function useNavigationShortcutKeys() {
|
|
54
|
+
const isMac = ref(false);
|
|
55
|
+
onMounted(() => isMac.value = isMacOS());
|
|
56
|
+
return {
|
|
57
|
+
label: computed(() => isMac.value ? "⌘K" : "Ctrl+K"),
|
|
58
|
+
ariaKeyshortcuts: computed(() => isMac.value ? "Meta+K" : "Control+K")
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Listens to Ctrl+K (⌘K on macOS) on `window` while mounted: opens the menu, waits for it to
|
|
63
|
+
* render (the drawer is `inert` while closed) and focuses the search field (a disabled field, as
|
|
64
|
+
* while loading, can't take the focus). When the menu opened by the shortcut closes with the focus
|
|
65
|
+
* inside it, the focus goes back to where it was.
|
|
66
|
+
*/
|
|
67
|
+
function useNavigationSearchShortcut(options) {
|
|
68
|
+
const { open, field } = options;
|
|
69
|
+
const owner = Symbol("navigation-search-shortcut");
|
|
70
|
+
/** Element focused before the shortcut opened the menu, which gets the focus back on close. */
|
|
71
|
+
let origin = null;
|
|
72
|
+
onMounted(() => {
|
|
73
|
+
owners.value.push(owner);
|
|
74
|
+
globalThis.addEventListener("keydown", onKeydown, { capture: true });
|
|
75
|
+
findInput().setAttribute("aria-label", SEARCH_NAME);
|
|
76
|
+
});
|
|
77
|
+
onUnmounted(() => {
|
|
78
|
+
owners.value = owners.value.filter((item) => item !== owner);
|
|
79
|
+
globalThis.removeEventListener("keydown", onKeydown, { capture: true });
|
|
80
|
+
});
|
|
81
|
+
watch(open, (isOpen) => {
|
|
82
|
+
if (isOpen) return;
|
|
83
|
+
const target = origin;
|
|
84
|
+
origin = null;
|
|
85
|
+
if (!findInput().closest(".v-navigation-drawer").contains(document.activeElement)) return;
|
|
86
|
+
if (target instanceof HTMLElement || target instanceof SVGElement) target.focus();
|
|
87
|
+
});
|
|
88
|
+
function onKeydown(event) {
|
|
89
|
+
if (owners.value[0] !== owner || isIgnoredTarget(event)) return;
|
|
90
|
+
if (isShortcut(event)) openAndFocus(event);
|
|
91
|
+
else if (event.key === "Escape" && origin) close();
|
|
92
|
+
}
|
|
93
|
+
async function openAndFocus(event) {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
if (!open.value) origin = document.activeElement;
|
|
96
|
+
open.value = true;
|
|
97
|
+
await nextTick();
|
|
98
|
+
const input = findInput();
|
|
99
|
+
input.focus({ preventScroll: true });
|
|
100
|
+
input.select();
|
|
101
|
+
}
|
|
102
|
+
function findInput() {
|
|
103
|
+
return toValue(field).$el.querySelector("input");
|
|
104
|
+
}
|
|
105
|
+
function close() {
|
|
106
|
+
open.value = false;
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
...useNavigationShortcutKeys(),
|
|
110
|
+
close
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function isMacOS() {
|
|
114
|
+
return navigator.userAgent.includes("Macintosh");
|
|
115
|
+
}
|
|
116
|
+
function isShortcut(event) {
|
|
117
|
+
return (isMacOS() ? event.metaKey : event.ctrlKey) && !event.shiftKey && !event.altKey && !event.isComposing && event.key.toLowerCase() === "k";
|
|
118
|
+
}
|
|
119
|
+
function isIgnoredTarget(event) {
|
|
120
|
+
return event.target instanceof Element && event.target.closest(IGNORED_TARGETS) !== null;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/composables/dimensions/dimensions.ts
|
|
124
|
+
/**
|
|
125
|
+
* Vuetify's utility class that stretches a component to the whole width of its parent, for
|
|
126
|
+
* components with a `fullWidth` prop.
|
|
127
|
+
*/
|
|
128
|
+
function useFullWidth(fullWidth) {
|
|
129
|
+
return computed(() => toValue(fullWidth) ? "w-100" : void 0);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Vuetify's utility class that stretches a component to the whole height of its parent, for
|
|
133
|
+
* components with a `fullHeight` prop. The parent must have a height of its own.
|
|
134
|
+
*/
|
|
135
|
+
function useFullHeight(fullHeight) {
|
|
136
|
+
return computed(() => toValue(fullHeight) ? "h-100" : void 0);
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/components/skeleton-loader/skeleton-loader.vue
|
|
140
|
+
var skeleton_loader_default = /* @__PURE__ */ defineComponent({
|
|
141
|
+
inheritAttrs: false,
|
|
142
|
+
__name: "skeleton-loader",
|
|
143
|
+
props: {
|
|
144
|
+
loading: {
|
|
145
|
+
type: Boolean,
|
|
146
|
+
required: false
|
|
147
|
+
},
|
|
148
|
+
type: {
|
|
149
|
+
type: null,
|
|
150
|
+
required: true
|
|
151
|
+
},
|
|
152
|
+
fullWidth: {
|
|
153
|
+
type: Boolean,
|
|
154
|
+
required: false
|
|
155
|
+
},
|
|
156
|
+
fullHeight: {
|
|
157
|
+
type: Boolean,
|
|
158
|
+
required: false
|
|
159
|
+
},
|
|
160
|
+
color: {
|
|
161
|
+
type: String,
|
|
162
|
+
required: false
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
setup(__props) {
|
|
166
|
+
const props = __props;
|
|
167
|
+
const { t } = useLocale();
|
|
168
|
+
const widthClass = useFullWidth(() => props.fullWidth);
|
|
169
|
+
const heightClass = useFullHeight(() => props.fullHeight);
|
|
170
|
+
const loadingAttrs = computed(() => ({
|
|
171
|
+
ariaLabel: void 0,
|
|
172
|
+
ariaLive: void 0,
|
|
173
|
+
"aria-label": t("$vuetify.loading"),
|
|
174
|
+
"aria-live": "polite"
|
|
175
|
+
}));
|
|
176
|
+
return (_ctx, _cache) => {
|
|
177
|
+
return openBlock(), createBlock(unref(VSkeletonLoader), mergeProps({
|
|
178
|
+
loading: props.loading,
|
|
179
|
+
type: props.type,
|
|
180
|
+
color: props.color,
|
|
181
|
+
class: [unref(widthClass), unref(heightClass)]
|
|
182
|
+
}, loadingAttrs.value), {
|
|
183
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
184
|
+
_: 3
|
|
185
|
+
}, 16, [
|
|
186
|
+
"loading",
|
|
187
|
+
"type",
|
|
188
|
+
"color",
|
|
189
|
+
"class"
|
|
190
|
+
]);
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
//#endregion
|
|
17
195
|
//#region src/components/app-bar/app-bar.vue?vue&type=script&setup=true&lang.ts
|
|
18
|
-
const _hoisted_1$
|
|
196
|
+
const _hoisted_1$3 = { class: "text-high-emphasis" };
|
|
197
|
+
const _hoisted_2$1 = { class: "text-medium-emphasis" };
|
|
19
198
|
//#endregion
|
|
20
199
|
//#region src/components/app-bar/app-bar.vue
|
|
21
200
|
var app_bar_default = /* @__PURE__ */ defineComponent({
|
|
@@ -49,6 +228,10 @@ var app_bar_default = /* @__PURE__ */ defineComponent({
|
|
|
49
228
|
logo: {
|
|
50
229
|
type: String,
|
|
51
230
|
required: false
|
|
231
|
+
},
|
|
232
|
+
loading: {
|
|
233
|
+
type: Boolean,
|
|
234
|
+
required: false
|
|
52
235
|
}
|
|
53
236
|
}, {
|
|
54
237
|
"notificationsOpen": {
|
|
@@ -64,6 +247,7 @@ var app_bar_default = /* @__PURE__ */ defineComponent({
|
|
|
64
247
|
const props = __props;
|
|
65
248
|
const notificationsOpen = useModel(__props, "notificationsOpen");
|
|
66
249
|
const navigationOpen = useModel(__props, "navigationOpen");
|
|
250
|
+
const { ariaKeyshortcuts: navigationKeyshortcuts } = useNavigationShortcutKeys();
|
|
67
251
|
const helpBtnDataTestid = computed(() => `${props.dataTestid}-help`);
|
|
68
252
|
const notificationsBtnDataTestid = computed(() => `${props.dataTestid}-notifications`);
|
|
69
253
|
const navigationBtnDataTestid = computed(() => `${props.dataTestid}-navigation`);
|
|
@@ -75,161 +259,238 @@ var app_bar_default = /* @__PURE__ */ defineComponent({
|
|
|
75
259
|
const showNotifications = computed(() => !!props.notifications);
|
|
76
260
|
const unreadCount = computed(() => props.notifications.filter((notification) => !notification.read).length);
|
|
77
261
|
const hasUnreadNotifications = computed(() => unreadCount.value > 0);
|
|
262
|
+
const unreadLabel = computed(() => unreadCount.value === 1 ? "1 não lida" : `${unreadCount.value} não lidas`);
|
|
263
|
+
const notificationsLabel = computed(() => hasUnreadNotifications.value ? `Notificações (${unreadLabel.value})` : "Notificações");
|
|
264
|
+
watch(() => props.loading, (loading) => {
|
|
265
|
+
if (loading) notificationsOpen.value = false;
|
|
266
|
+
});
|
|
78
267
|
const showUser = computed(() => !!props.user);
|
|
79
|
-
const
|
|
268
|
+
const userInitials = computed(() => toInitials(props.user.title ?? ""));
|
|
269
|
+
const userAvatarProps = computed(() => {
|
|
270
|
+
if (props.user.img) return { image: props.user.img };
|
|
271
|
+
if (userInitials.value) return { text: userInitials.value };
|
|
272
|
+
return { icon: "mdi-account-outline" };
|
|
273
|
+
});
|
|
80
274
|
function isParentOption(option) {
|
|
81
275
|
return "items" in option;
|
|
82
276
|
}
|
|
83
|
-
function formatNotificationDate(date) {
|
|
84
|
-
const interval = toPeriodInterval(date);
|
|
85
|
-
if (wasThisMonth(interval)) {
|
|
86
|
-
if (wasToday(interval)) return formatDateTime(date, "HH:mm");
|
|
87
|
-
else if (wasYesterday(interval)) return "Ontem";
|
|
88
|
-
}
|
|
89
|
-
return formatDateTime(date, "DD/MM/YYYY");
|
|
90
|
-
}
|
|
91
|
-
function wasThisMonth(interval) {
|
|
92
|
-
return interval.months === 0 && interval.years === 0;
|
|
93
|
-
}
|
|
94
|
-
function wasToday(interval) {
|
|
95
|
-
return interval.days === 0;
|
|
96
|
-
}
|
|
97
|
-
function wasYesterday(interval) {
|
|
98
|
-
return interval.days === 1;
|
|
99
|
-
}
|
|
100
277
|
return (_ctx, _cache) => {
|
|
101
|
-
return openBlock(), createBlock(unref(VAppBar), {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}, {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
278
|
+
return openBlock(), createBlock(unref(VAppBar), {
|
|
279
|
+
"data-testid": props.dataTestid,
|
|
280
|
+
"aria-busy": props.loading || void 0
|
|
281
|
+
}, {
|
|
282
|
+
append: withCtx(() => [createVNode(skeleton_loader_default, {
|
|
283
|
+
loading: props.loading,
|
|
284
|
+
type: "avatar"
|
|
285
|
+
}, {
|
|
286
|
+
default: withCtx(() => [
|
|
287
|
+
showHelp.value ? (openBlock(), createBlock(unref(VBtn), mergeProps({ key: 0 }, unref(routeProps), {
|
|
288
|
+
"data-testid": helpBtnDataTestid.value,
|
|
289
|
+
"aria-label": "Ajuda",
|
|
290
|
+
icon: "mdi-help-circle-outline"
|
|
291
|
+
}), null, 16, ["data-testid"])) : createCommentVNode("v-if", true),
|
|
292
|
+
showNotifications.value ? (openBlock(), createBlock(unref(VMenu), {
|
|
293
|
+
key: 1,
|
|
294
|
+
modelValue: notificationsOpen.value,
|
|
295
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => notificationsOpen.value = $event),
|
|
296
|
+
"close-on-content-click": false
|
|
297
|
+
}, {
|
|
298
|
+
activator: withCtx(({ props }) => [createVNode(unref(VBtn), mergeProps(props, {
|
|
299
|
+
"data-testid": notificationsBtnDataTestid.value,
|
|
300
|
+
"aria-label": notificationsLabel.value,
|
|
301
|
+
icon: ""
|
|
302
|
+
}), {
|
|
303
|
+
default: withCtx(() => [createVNode(unref(VBadge), {
|
|
304
|
+
"model-value": hasUnreadNotifications.value,
|
|
305
|
+
content: unreadCount.value,
|
|
306
|
+
label: unreadLabel.value,
|
|
307
|
+
color: "error"
|
|
308
|
+
}, {
|
|
309
|
+
default: withCtx(() => [createVNode(unref(VIcon), { icon: "mdi-bell-outline" })]),
|
|
131
310
|
_: 1
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
subtitle: notification.subtitle,
|
|
138
|
-
"active-class": "text-primary"
|
|
139
|
-
}, createSlots({ _: 2 }, [notification.date ? {
|
|
140
|
-
name: "append",
|
|
141
|
-
fn: withCtx(() => [createVNode(unref(VListItemAction), { end: "" }, {
|
|
142
|
-
default: withCtx(() => [createElementVNode("small", _hoisted_1$2, toDisplayString(formatNotificationDate(notification.date)), 1)]),
|
|
143
|
-
_: 2
|
|
144
|
-
}, 1024)]),
|
|
145
|
-
key: "0"
|
|
146
|
-
} : void 0]), 1032, [
|
|
147
|
-
"active",
|
|
148
|
-
"title",
|
|
149
|
-
"subtitle"
|
|
150
|
-
]);
|
|
151
|
-
}), 128))]),
|
|
311
|
+
}, 8, [
|
|
312
|
+
"model-value",
|
|
313
|
+
"content",
|
|
314
|
+
"label"
|
|
315
|
+
])]),
|
|
152
316
|
_: 1
|
|
153
|
-
}
|
|
154
|
-
|
|
317
|
+
}, 16, ["data-testid", "aria-label"])]),
|
|
318
|
+
default: withCtx(() => [createVNode(unref(VCard), { "data-testid": notificationsMenuDataTestid.value }, {
|
|
319
|
+
default: withCtx(() => [props.notifications?.length ? (openBlock(), createBlock(unref(VList), { key: 0 }, {
|
|
320
|
+
default: withCtx(() => [createVNode(unref(VListSubheader), null, {
|
|
321
|
+
default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode("Notificações", -1)])]),
|
|
322
|
+
_: 1
|
|
323
|
+
}), (openBlock(true), createElementBlock(Fragment, null, renderList(props.notifications, (notification, index) => {
|
|
324
|
+
return openBlock(), createBlock(unref(VListItem), {
|
|
325
|
+
key: index,
|
|
326
|
+
active: !notification.read,
|
|
327
|
+
title: notification.title,
|
|
328
|
+
subtitle: notification.subtitle,
|
|
329
|
+
"active-class": "text-primary-text"
|
|
330
|
+
}, createSlots({
|
|
331
|
+
subtitle: withCtx(({ subtitle }) => [createElementVNode("span", _hoisted_1$3, toDisplayString(subtitle), 1)]),
|
|
332
|
+
_: 2
|
|
333
|
+
}, [notification.when ? {
|
|
334
|
+
name: "append",
|
|
335
|
+
fn: withCtx(() => [createVNode(unref(VListItemAction), { end: "" }, {
|
|
336
|
+
default: withCtx(() => [createCommentVNode(" No prop sets the secondary text color, which follows the theme's contrast "), createElementVNode("small", _hoisted_2$1, toDisplayString(notification.when), 1)]),
|
|
337
|
+
_: 2
|
|
338
|
+
}, 1024)]),
|
|
339
|
+
key: "0"
|
|
340
|
+
} : void 0]), 1032, [
|
|
341
|
+
"active",
|
|
342
|
+
"title",
|
|
343
|
+
"subtitle"
|
|
344
|
+
]);
|
|
345
|
+
}), 128))]),
|
|
346
|
+
_: 1
|
|
347
|
+
})) : (openBlock(), createBlock(unref(VCardText), { key: 1 }, {
|
|
348
|
+
default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode("Nenhuma notificação", -1)])]),
|
|
349
|
+
_: 1
|
|
350
|
+
}))]),
|
|
155
351
|
_: 1
|
|
156
|
-
})
|
|
352
|
+
}, 8, ["data-testid"])]),
|
|
157
353
|
_: 1
|
|
158
|
-
}, 8, ["
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
key: 2,
|
|
163
|
-
"close-on-content-click": false
|
|
164
|
-
}, {
|
|
165
|
-
activator: withCtx(({ props }) => [createVNode(unref(VBtn), mergeProps(props, {
|
|
166
|
-
"data-testid": userBtnDataTestid.value,
|
|
167
|
-
icon: ""
|
|
168
|
-
}), {
|
|
169
|
-
default: withCtx(() => [createVNode(unref(VAvatar), mergeProps(userAvatarProps.value, { color: "primary" }), null, 16)]),
|
|
170
|
-
_: 1
|
|
171
|
-
}, 16, ["data-testid"])]),
|
|
172
|
-
default: withCtx(() => [createVNode(unref(VCard), {
|
|
173
|
-
title: __props.user?.title,
|
|
174
|
-
subtitle: __props.user?.subtitle,
|
|
175
|
-
"data-testid": userMenuDataTestid.value
|
|
354
|
+
}, 8, ["modelValue"])) : createCommentVNode("v-if", true),
|
|
355
|
+
showUser.value ? (openBlock(), createBlock(unref(VMenu), {
|
|
356
|
+
key: 2,
|
|
357
|
+
"close-on-content-click": false
|
|
176
358
|
}, {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}),
|
|
359
|
+
activator: withCtx(({ props }) => [createVNode(unref(VBtn), mergeProps(props, {
|
|
360
|
+
"data-testid": userBtnDataTestid.value,
|
|
361
|
+
"aria-label": "Menu do usuário",
|
|
362
|
+
icon: ""
|
|
363
|
+
}), {
|
|
364
|
+
default: withCtx(() => [createCommentVNode(" Decorative: the button already has an accessible name "), createVNode(unref(VAvatar), mergeProps(userAvatarProps.value, {
|
|
365
|
+
color: "primary",
|
|
366
|
+
"aria-hidden": "true"
|
|
367
|
+
}), null, 16)]),
|
|
368
|
+
_: 1
|
|
369
|
+
}, 16, ["data-testid"])]),
|
|
370
|
+
default: withCtx(() => [createVNode(unref(VCard), {
|
|
371
|
+
title: __props.user?.title,
|
|
372
|
+
subtitle: __props.user?.subtitle,
|
|
373
|
+
"data-testid": userMenuDataTestid.value
|
|
374
|
+
}, {
|
|
375
|
+
prepend: withCtx(() => [createCommentVNode(" Decorative: the user's name, when given, is already the card's title "), createVNode(unref(VAvatar), mergeProps(userAvatarProps.value, {
|
|
376
|
+
color: "primary",
|
|
377
|
+
"aria-hidden": "true"
|
|
378
|
+
}), null, 16)]),
|
|
379
|
+
default: withCtx(() => [createVNode(unref(VDivider)), createVNode(unref(VList), {
|
|
380
|
+
nav: "",
|
|
381
|
+
role: "group"
|
|
382
|
+
}, {
|
|
383
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.user?.options, (option, index) => {
|
|
384
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [index > 0 ? (openBlock(), createBlock(unref(VDivider), {
|
|
385
|
+
key: 0,
|
|
386
|
+
class: "mt-1"
|
|
387
|
+
})) : createCommentVNode("v-if", true), isParentOption(option) ? (openBlock(), createBlock(unref(VListGroup), { key: 1 }, {
|
|
388
|
+
activator: withCtx(({ props, isOpen }) => [createVNode(unref(VListItem), mergeProps({ ref_for: true }, props, {
|
|
389
|
+
title: option.description,
|
|
390
|
+
"aria-expanded": isOpen,
|
|
391
|
+
role: void 0,
|
|
392
|
+
tag: "button",
|
|
393
|
+
type: "button"
|
|
394
|
+
}), null, 16, ["title", "aria-expanded"])]),
|
|
395
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(option.items, (childOption, index) => {
|
|
396
|
+
return openBlock(), createBlock(unref(VListItem), mergeProps({ key: index }, { ref_for: true }, unref(routeToListItemProps)(childOption.route), {
|
|
397
|
+
title: childOption.description,
|
|
398
|
+
onClick: childOption.action
|
|
399
|
+
}), null, 16, ["title", "onClick"]);
|
|
400
|
+
}), 128))]),
|
|
401
|
+
_: 2
|
|
402
|
+
}, 1024)) : (openBlock(), createBlock(unref(VListItem), mergeProps({
|
|
403
|
+
key: 2,
|
|
404
|
+
title: option.description
|
|
405
|
+
}, { ref_for: true }, unref(routeToListItemProps)(option.route), { onClick: option.action }), null, 16, ["title", "onClick"]))], 64);
|
|
406
|
+
}), 128))]),
|
|
407
|
+
_: 1
|
|
408
|
+
})]),
|
|
197
409
|
_: 1
|
|
198
|
-
}
|
|
410
|
+
}, 8, [
|
|
411
|
+
"title",
|
|
412
|
+
"subtitle",
|
|
413
|
+
"data-testid"
|
|
414
|
+
])]),
|
|
199
415
|
_: 1
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
])]),
|
|
205
|
-
_: 1
|
|
206
|
-
})) : createCommentVNode("v-if", true)
|
|
207
|
-
]),
|
|
416
|
+
})) : createCommentVNode("v-if", true)
|
|
417
|
+
]),
|
|
418
|
+
_: 1
|
|
419
|
+
}, 8, ["loading"])]),
|
|
208
420
|
default: withCtx(() => [
|
|
209
421
|
__props.navigation ? (openBlock(), createBlock(unref(VAppBarNavIcon), {
|
|
210
422
|
key: 0,
|
|
211
423
|
"data-testid": navigationBtnDataTestid.value,
|
|
424
|
+
"aria-expanded": navigationOpen.value,
|
|
425
|
+
"aria-label": "Menu de navegação",
|
|
426
|
+
"aria-keyshortcuts": unref(navigationKeyshortcuts),
|
|
212
427
|
onClick: _cache[0] || (_cache[0] = ($event) => navigationOpen.value = !navigationOpen.value)
|
|
213
|
-
}, null, 8, [
|
|
428
|
+
}, null, 8, [
|
|
429
|
+
"data-testid",
|
|
430
|
+
"aria-expanded",
|
|
431
|
+
"aria-keyshortcuts"
|
|
432
|
+
])) : createCommentVNode("v-if", true),
|
|
214
433
|
__props.logo ? (openBlock(), createBlock(unref(VImg), {
|
|
215
434
|
key: 1,
|
|
216
435
|
src: props.logo,
|
|
436
|
+
alt: "",
|
|
217
437
|
height: "100%",
|
|
218
438
|
width: "fit-content",
|
|
219
439
|
class: "py-2 ml-5 mr-n3",
|
|
220
440
|
cover: ""
|
|
221
441
|
}, null, 8, ["src"])) : createCommentVNode("v-if", true),
|
|
222
|
-
props.title ? (openBlock(), createBlock(unref(VAppBarTitle), {
|
|
442
|
+
props.title ? (openBlock(), createBlock(unref(VAppBarTitle), {
|
|
443
|
+
key: 2,
|
|
444
|
+
role: "heading",
|
|
445
|
+
"aria-level": "1"
|
|
446
|
+
}, {
|
|
223
447
|
default: withCtx(() => [createTextVNode(toDisplayString(props.title), 1)]),
|
|
224
448
|
_: 1
|
|
225
449
|
})) : createCommentVNode("v-if", true)
|
|
226
450
|
]),
|
|
227
451
|
_: 1
|
|
228
|
-
}, 8, ["data-testid"]);
|
|
452
|
+
}, 8, ["data-testid", "aria-busy"]);
|
|
229
453
|
};
|
|
230
454
|
}
|
|
231
455
|
});
|
|
232
456
|
//#endregion
|
|
457
|
+
//#region src/consts/icons.ts
|
|
458
|
+
/**
|
|
459
|
+
* Icon font class of each icon available to the components that accept one (e.g. `UIconButton`
|
|
460
|
+
* and the items of the navigation menu), by its name, in the order the "Ícones" guide page lists
|
|
461
|
+
* them. A name ending in `-alternative` is an alternative version of the same drawing (currently,
|
|
462
|
+
* outlined).
|
|
463
|
+
*
|
|
464
|
+
* To add an icon, add it here and to the list on the "Ícones" guide page (`docs/guide/icons.md`).
|
|
465
|
+
*/
|
|
466
|
+
const icons = {
|
|
467
|
+
home: "mdi-home",
|
|
468
|
+
"home-alternative": "mdi-home-outline",
|
|
469
|
+
account: "mdi-account",
|
|
470
|
+
"account-alternative": "mdi-account-outline",
|
|
471
|
+
"account-group": "mdi-account-group",
|
|
472
|
+
"account-group-alternative": "mdi-account-group-outline",
|
|
473
|
+
calendar: "mdi-calendar",
|
|
474
|
+
"calendar-alternative": "mdi-calendar-outline",
|
|
475
|
+
"clipboard-text": "mdi-clipboard-text",
|
|
476
|
+
"clipboard-text-alternative": "mdi-clipboard-text-outline",
|
|
477
|
+
"file-document": "mdi-file-document",
|
|
478
|
+
"file-document-alternative": "mdi-file-document-outline",
|
|
479
|
+
flask: "mdi-flask",
|
|
480
|
+
"flask-alternative": "mdi-flask-outline",
|
|
481
|
+
folder: "mdi-folder",
|
|
482
|
+
"folder-alternative": "mdi-folder-outline",
|
|
483
|
+
"chart-box": "mdi-chart-box",
|
|
484
|
+
"chart-box-alternative": "mdi-chart-box-outline",
|
|
485
|
+
wallet: "mdi-wallet",
|
|
486
|
+
"wallet-alternative": "mdi-wallet-outline",
|
|
487
|
+
cog: "mdi-cog",
|
|
488
|
+
"cog-alternative": "mdi-cog-outline",
|
|
489
|
+
stethoscope: "mdi-stethoscope",
|
|
490
|
+
pill: "mdi-pill",
|
|
491
|
+
hospital: "mdi-hospital-building"
|
|
492
|
+
};
|
|
493
|
+
//#endregion
|
|
233
494
|
//#region src/composables/inputs/rules.ts
|
|
234
495
|
function required(value) {
|
|
235
496
|
return !isEmpty(value) || "Campo obrigatório";
|
|
@@ -244,7 +505,23 @@ function url(value) {
|
|
|
244
505
|
return isEmpty(value) || isUrl(value) || "URL inválida";
|
|
245
506
|
}
|
|
246
507
|
//#endregion
|
|
247
|
-
//#region src/composables/inputs/
|
|
508
|
+
//#region src/composables/inputs/fields.ts
|
|
509
|
+
const textFieldVariantToVuetifyVariant = {
|
|
510
|
+
primary: "underlined",
|
|
511
|
+
secondary: "outlined"
|
|
512
|
+
};
|
|
513
|
+
function useTextFieldVariant(variant) {
|
|
514
|
+
return computed(() => {
|
|
515
|
+
const variantValue = toValue(variant);
|
|
516
|
+
return textFieldVariantToVuetifyVariant[variantValue];
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
function createRequiredRule(required, isPresent) {
|
|
520
|
+
return (value) => {
|
|
521
|
+
if (!required) return true;
|
|
522
|
+
return isPresent(value) || "Campo obrigatório";
|
|
523
|
+
};
|
|
524
|
+
}
|
|
248
525
|
function useRules(options) {
|
|
249
526
|
return computed(() => {
|
|
250
527
|
const rules = [];
|
|
@@ -254,10 +531,6 @@ function useRules(options) {
|
|
|
254
531
|
}
|
|
255
532
|
//#endregion
|
|
256
533
|
//#region src/composables/inputs/text-field.ts
|
|
257
|
-
const textFieldVariantToVuetifyVariant = {
|
|
258
|
-
primary: "underlined",
|
|
259
|
-
secondary: "outlined"
|
|
260
|
-
};
|
|
261
534
|
const textFieldTypeToVuetifyType = {
|
|
262
535
|
text: "text",
|
|
263
536
|
phone: "tel",
|
|
@@ -271,18 +544,55 @@ const textFieldTypeToVuetifyRule = {
|
|
|
271
544
|
email,
|
|
272
545
|
url
|
|
273
546
|
};
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
function useTextFieldType(type) {
|
|
547
|
+
/**
|
|
548
|
+
* Maps the field type to the input type, showing as plain text a password
|
|
549
|
+
* the user chose to make visible.
|
|
550
|
+
*/
|
|
551
|
+
function useTextFieldType(type, isPasswordVisible) {
|
|
281
552
|
return computed(() => {
|
|
282
553
|
const typeValue = toValue(type);
|
|
554
|
+
if (typeValue === "password" && toValue(isPasswordVisible)) return "text";
|
|
283
555
|
return textFieldTypeToVuetifyType[typeValue];
|
|
284
556
|
});
|
|
285
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* Controls the icon at the end of the field: a decorative magnifier on
|
|
560
|
+
* `search`, and the toggle that shows or hides the value on `password`. The
|
|
561
|
+
* toggle works with click, `Enter` and `Space`, does nothing while disabled,
|
|
562
|
+
* and the password hides again whenever the type stops being `password`.
|
|
563
|
+
*/
|
|
564
|
+
function useTextFieldAppendIcon(type, disabled) {
|
|
565
|
+
const isPasswordField = computed(() => toValue(type) === "password");
|
|
566
|
+
const isPasswordVisible = ref(false);
|
|
567
|
+
watch(isPasswordField, () => {
|
|
568
|
+
isPasswordVisible.value = false;
|
|
569
|
+
});
|
|
570
|
+
function toggle() {
|
|
571
|
+
if (toValue(disabled)) return;
|
|
572
|
+
isPasswordVisible.value = !isPasswordVisible.value;
|
|
573
|
+
}
|
|
574
|
+
function onKeydown(event) {
|
|
575
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
576
|
+
event.preventDefault();
|
|
577
|
+
event.stopPropagation();
|
|
578
|
+
toggle();
|
|
579
|
+
}
|
|
580
|
+
const iconProps = computed(() => {
|
|
581
|
+
if (toValue(type) === "search") return { icon: "mdi-magnify" };
|
|
582
|
+
if (!isPasswordField.value) return void 0;
|
|
583
|
+
return {
|
|
584
|
+
icon: isPasswordVisible.value ? "mdi-eye-off" : "mdi-eye",
|
|
585
|
+
"aria-label": "Mostrar senha",
|
|
586
|
+
"aria-pressed": isPasswordVisible.value,
|
|
587
|
+
onClick: toggle,
|
|
588
|
+
onKeydown
|
|
589
|
+
};
|
|
590
|
+
});
|
|
591
|
+
return {
|
|
592
|
+
isPasswordVisible: readonly(isPasswordVisible),
|
|
593
|
+
iconProps
|
|
594
|
+
};
|
|
595
|
+
}
|
|
286
596
|
function useTextFieldRules(props) {
|
|
287
597
|
const defaultRules = useRules(props);
|
|
288
598
|
return computed(() => {
|
|
@@ -296,6 +606,34 @@ function associatedRuleType(type) {
|
|
|
296
606
|
return type in textFieldTypeToVuetifyRule;
|
|
297
607
|
}
|
|
298
608
|
//#endregion
|
|
609
|
+
//#region src/components/inputs/field-loader.vue
|
|
610
|
+
var field_loader_default = /* @__PURE__ */ defineComponent({
|
|
611
|
+
inheritAttrs: false,
|
|
612
|
+
__name: "field-loader",
|
|
613
|
+
props: {
|
|
614
|
+
color: {
|
|
615
|
+
type: String,
|
|
616
|
+
required: false
|
|
617
|
+
},
|
|
618
|
+
isActive: {
|
|
619
|
+
type: Boolean,
|
|
620
|
+
required: true
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
setup(__props) {
|
|
624
|
+
const props = __props;
|
|
625
|
+
return (_ctx, _cache) => {
|
|
626
|
+
return openBlock(), createBlock(unref(VProgressLinear), {
|
|
627
|
+
active: props.isActive,
|
|
628
|
+
color: props.color,
|
|
629
|
+
height: "2",
|
|
630
|
+
indeterminate: "",
|
|
631
|
+
"aria-hidden": "true"
|
|
632
|
+
}, null, 8, ["active", "color"]);
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
//#endregion
|
|
299
637
|
//#region src/components/inputs/text-field/text-field.vue
|
|
300
638
|
var text_field_default = /* @__PURE__ */ defineComponent({
|
|
301
639
|
inheritAttrs: false,
|
|
@@ -360,11 +698,10 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
360
698
|
const modelValue = useModel(__props, "modelValue");
|
|
361
699
|
const props = __props;
|
|
362
700
|
const vuetifyVariant = useTextFieldVariant(() => props.variant);
|
|
363
|
-
const
|
|
701
|
+
const { isPasswordVisible, iconProps: appendIconProps } = useTextFieldAppendIcon(() => props.type, () => props.disabled);
|
|
702
|
+
const vuetifyType = useTextFieldType(() => props.type, isPasswordVisible);
|
|
364
703
|
const rules = useTextFieldRules(props);
|
|
365
|
-
const
|
|
366
|
-
const computedClearable = computed(() => props.clearable ?? isSearchField.value);
|
|
367
|
-
const appendIcon = computed(() => isSearchField.value ? "mdi-magnify" : void 0);
|
|
704
|
+
const computedClearable = computed(() => props.clearable ?? props.type === "search");
|
|
368
705
|
function clear() {
|
|
369
706
|
modelValue.value = "";
|
|
370
707
|
}
|
|
@@ -374,26 +711,33 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
374
711
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
|
|
375
712
|
variant: unref(vuetifyVariant),
|
|
376
713
|
type: unref(vuetifyType),
|
|
377
|
-
"append-inner-icon": appendIcon.value,
|
|
378
714
|
rules: unref(rules),
|
|
379
715
|
"data-testid": props.dataTestid,
|
|
380
716
|
disabled: props.disabled,
|
|
381
717
|
loading: props.loading,
|
|
718
|
+
"aria-busy": props.loading || void 0,
|
|
382
719
|
readonly: props.readonly,
|
|
383
720
|
label: props.label,
|
|
384
721
|
placeholder: props.placeholder,
|
|
385
722
|
hint: props.hint,
|
|
386
723
|
clearable: computedClearable.value,
|
|
387
724
|
"onClick:clear": clear
|
|
388
|
-
},
|
|
725
|
+
}, createSlots({
|
|
726
|
+
loader: withCtx((loader) => [createVNode(field_loader_default, normalizeProps(guardReactiveProps(loader)), null, 16)]),
|
|
727
|
+
_: 2
|
|
728
|
+
}, [unref(appendIconProps) ? {
|
|
729
|
+
name: "append-inner",
|
|
730
|
+
fn: withCtx(() => [createVNode(unref(VIcon), mergeProps(unref(appendIconProps), { disabled: props.disabled }), null, 16, ["disabled"])]),
|
|
731
|
+
key: "0"
|
|
732
|
+
} : void 0]), 1032, [
|
|
389
733
|
"modelValue",
|
|
390
734
|
"variant",
|
|
391
735
|
"type",
|
|
392
|
-
"append-inner-icon",
|
|
393
736
|
"rules",
|
|
394
737
|
"data-testid",
|
|
395
738
|
"disabled",
|
|
396
739
|
"loading",
|
|
740
|
+
"aria-busy",
|
|
397
741
|
"readonly",
|
|
398
742
|
"label",
|
|
399
743
|
"placeholder",
|
|
@@ -405,7 +749,7 @@ var text_field_default = /* @__PURE__ */ defineComponent({
|
|
|
405
749
|
});
|
|
406
750
|
//#endregion
|
|
407
751
|
//#region src/components/navigation-menu/navigation-menu.vue?vue&type=script&setup=true&lang.ts
|
|
408
|
-
const _hoisted_1$
|
|
752
|
+
const _hoisted_1$2 = { class: "px-4 pt-2" };
|
|
409
753
|
//#endregion
|
|
410
754
|
//#region src/components/navigation-menu/navigation-menu.vue
|
|
411
755
|
var navigation_menu_default = /* @__PURE__ */ defineComponent({
|
|
@@ -419,6 +763,10 @@ var navigation_menu_default = /* @__PURE__ */ defineComponent({
|
|
|
419
763
|
items: {
|
|
420
764
|
type: Array,
|
|
421
765
|
required: false
|
|
766
|
+
},
|
|
767
|
+
loading: {
|
|
768
|
+
type: Boolean,
|
|
769
|
+
required: false
|
|
422
770
|
}
|
|
423
771
|
}, {
|
|
424
772
|
"modelValue": {
|
|
@@ -432,6 +780,10 @@ var navigation_menu_default = /* @__PURE__ */ defineComponent({
|
|
|
432
780
|
const props = __props;
|
|
433
781
|
const open = useModel(__props, "modelValue");
|
|
434
782
|
const search = ref("");
|
|
783
|
+
const { label: shortcutLabel, close } = useNavigationSearchShortcut({
|
|
784
|
+
open,
|
|
785
|
+
field: useTemplateRef("searchField")
|
|
786
|
+
});
|
|
435
787
|
const filteredItems = computed(() => filterItems());
|
|
436
788
|
const searchDataTestid = computed(() => `${props.dataTestid}-search`);
|
|
437
789
|
const query = computed(() => search.value.toLowerCase());
|
|
@@ -458,37 +810,112 @@ var navigation_menu_default = /* @__PURE__ */ defineComponent({
|
|
|
458
810
|
return openBlock(), createBlock(unref(VNavigationDrawer), {
|
|
459
811
|
modelValue: open.value,
|
|
460
812
|
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => open.value = $event),
|
|
813
|
+
color: "primary",
|
|
461
814
|
"data-testid": props.dataTestid,
|
|
815
|
+
"aria-busy": props.loading || void 0,
|
|
462
816
|
absolute: "",
|
|
463
|
-
temporary: ""
|
|
817
|
+
temporary: "",
|
|
818
|
+
onKeydown: withKeys(unref(close), ["esc"])
|
|
464
819
|
}, {
|
|
465
|
-
prepend: withCtx(() => [createElementVNode("div", _hoisted_1$
|
|
820
|
+
prepend: withCtx(() => [createElementVNode("div", _hoisted_1$2, [createVNode(text_field_default, {
|
|
821
|
+
ref: "searchField",
|
|
466
822
|
modelValue: search.value,
|
|
467
823
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
|
|
468
824
|
type: "search",
|
|
469
|
-
|
|
470
|
-
"data-testid": searchDataTestid.value
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
825
|
+
placeholder: `Buscar (${unref(shortcutLabel)})`,
|
|
826
|
+
"data-testid": searchDataTestid.value,
|
|
827
|
+
disabled: props.loading
|
|
828
|
+
}, null, 8, [
|
|
829
|
+
"modelValue",
|
|
830
|
+
"placeholder",
|
|
831
|
+
"data-testid",
|
|
832
|
+
"disabled"
|
|
833
|
+
])])]),
|
|
834
|
+
default: withCtx(() => [createVNode(unref(VDivider)), createVNode(skeleton_loader_default, {
|
|
835
|
+
loading: props.loading,
|
|
836
|
+
type: "list-item@6",
|
|
837
|
+
color: "primary"
|
|
838
|
+
}, {
|
|
839
|
+
default: withCtx(() => [createCommentVNode(" Vuetify's `list` role only allows list items, but the items are links "), createVNode(unref(VList), {
|
|
840
|
+
nav: "",
|
|
841
|
+
role: "group"
|
|
842
|
+
}, {
|
|
843
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(filteredItems.value, (item, index) => {
|
|
844
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [isParentItem(item) ? (openBlock(), createBlock(unref(VListGroup), { key: 0 }, {
|
|
845
|
+
activator: withCtx(({ props: activatorProps, isOpen }) => [createVNode(unref(VListItem), mergeProps({ ref_for: true }, activatorProps, {
|
|
846
|
+
title: item.description,
|
|
847
|
+
"prepend-icon": item.icon && unref(icons)[item.icon],
|
|
848
|
+
"aria-expanded": isOpen,
|
|
849
|
+
role: void 0,
|
|
850
|
+
tag: "button",
|
|
851
|
+
type: "button"
|
|
852
|
+
}), null, 16, [
|
|
853
|
+
"title",
|
|
854
|
+
"prepend-icon",
|
|
855
|
+
"aria-expanded"
|
|
856
|
+
])]),
|
|
857
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(item.items, (childItem, childIndex) => {
|
|
858
|
+
return openBlock(), createBlock(unref(VListItem), mergeProps({ key: childIndex }, { ref_for: true }, unref(routeToListItemProps)(childItem.route), {
|
|
859
|
+
title: childItem.description,
|
|
860
|
+
"prepend-icon": childItem.icon && unref(icons)[childItem.icon],
|
|
861
|
+
onClick: childItem.action
|
|
862
|
+
}), null, 16, [
|
|
863
|
+
"title",
|
|
864
|
+
"prepend-icon",
|
|
865
|
+
"onClick"
|
|
866
|
+
]);
|
|
867
|
+
}), 128))]),
|
|
868
|
+
_: 2
|
|
869
|
+
}, 1024)) : (openBlock(), createBlock(unref(VListItem), mergeProps({
|
|
870
|
+
key: 1,
|
|
871
|
+
title: item.description,
|
|
872
|
+
"prepend-icon": item.icon && unref(icons)[item.icon]
|
|
873
|
+
}, { ref_for: true }, unref(routeToListItemProps)(item.route), { onClick: item.action }), null, 16, [
|
|
874
|
+
"title",
|
|
875
|
+
"prepend-icon",
|
|
876
|
+
"onClick"
|
|
877
|
+
]))], 64);
|
|
878
|
+
}), 128))]),
|
|
879
|
+
_: 1
|
|
880
|
+
})]),
|
|
488
881
|
_: 1
|
|
489
|
-
})]),
|
|
882
|
+
}, 8, ["loading"])]),
|
|
883
|
+
_: 1
|
|
884
|
+
}, 8, [
|
|
885
|
+
"modelValue",
|
|
886
|
+
"data-testid",
|
|
887
|
+
"aria-busy",
|
|
888
|
+
"onKeydown"
|
|
889
|
+
]);
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
//#endregion
|
|
894
|
+
//#region src/components/footer/footer.vue
|
|
895
|
+
var footer_default = /* @__PURE__ */ defineComponent({
|
|
896
|
+
inheritAttrs: false,
|
|
897
|
+
__name: "footer",
|
|
898
|
+
props: {
|
|
899
|
+
description: {
|
|
900
|
+
type: String,
|
|
901
|
+
required: false
|
|
902
|
+
},
|
|
903
|
+
dataTestid: {
|
|
904
|
+
type: String,
|
|
905
|
+
required: false
|
|
906
|
+
}
|
|
907
|
+
},
|
|
908
|
+
setup(__props) {
|
|
909
|
+
const props = __props;
|
|
910
|
+
return (_ctx, _cache) => {
|
|
911
|
+
return openBlock(), createBlock(unref(VFooter), {
|
|
912
|
+
"data-testid": props.dataTestid,
|
|
913
|
+
class: "justify-center text-center",
|
|
914
|
+
app: ""
|
|
915
|
+
}, {
|
|
916
|
+
default: withCtx(() => [createTextVNode(toDisplayString(props.description), 1)]),
|
|
490
917
|
_: 1
|
|
491
|
-
}, 8, ["
|
|
918
|
+
}, 8, ["data-testid"]);
|
|
492
919
|
};
|
|
493
920
|
}
|
|
494
921
|
});
|
|
@@ -497,33 +924,67 @@ var navigation_menu_default = /* @__PURE__ */ defineComponent({
|
|
|
497
924
|
var container_default = /* @__PURE__ */ defineComponent({
|
|
498
925
|
inheritAttrs: false,
|
|
499
926
|
__name: "container",
|
|
500
|
-
props: {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
927
|
+
props: {
|
|
928
|
+
fullHeight: {
|
|
929
|
+
type: Boolean,
|
|
930
|
+
required: false
|
|
931
|
+
},
|
|
932
|
+
dataTestid: {
|
|
933
|
+
type: String,
|
|
934
|
+
required: false
|
|
935
|
+
}
|
|
936
|
+
},
|
|
504
937
|
setup(__props) {
|
|
505
938
|
const props = __props;
|
|
939
|
+
const heightClass = useFullHeight(() => props.fullHeight);
|
|
506
940
|
return (_ctx, _cache) => {
|
|
507
941
|
return openBlock(), createBlock(unref(VContainer), {
|
|
942
|
+
class: normalizeClass(unref(heightClass)),
|
|
508
943
|
"data-testid": props.dataTestid,
|
|
509
944
|
fluid: ""
|
|
510
945
|
}, {
|
|
511
946
|
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
512
947
|
_: 3
|
|
513
|
-
}, 8, ["data-testid"]);
|
|
948
|
+
}, 8, ["class", "data-testid"]);
|
|
514
949
|
};
|
|
515
950
|
}
|
|
516
951
|
});
|
|
517
952
|
//#endregion
|
|
953
|
+
//#region src/composables/dialogs/use-decorative-timer.ts
|
|
954
|
+
/**
|
|
955
|
+
* Content props that hide a snackbar's timer bar from assistive technologies,
|
|
956
|
+
* keeping it only as a visual countdown, since the message itself is already
|
|
957
|
+
* announced by the snackbar's live region.
|
|
958
|
+
*
|
|
959
|
+
* Workaround until Vuetify offers an option for it: the timer bar is a
|
|
960
|
+
* `VProgressLinear` that always renders `role="progressbar"` with
|
|
961
|
+
* `aria-hidden="false"` and no accessible name, and neither `VSnackbar` nor
|
|
962
|
+
* Vuetify's defaults can set attributes on it. So these vnode hooks mark the
|
|
963
|
+
* bar after every render of the snackbar content, which also covers the bar
|
|
964
|
+
* recreated when the pointer leaves the snackbar. Vue doesn't patch the
|
|
965
|
+
* attribute back, since Vuetify keeps rendering the same `"false"` value.
|
|
966
|
+
*/
|
|
967
|
+
function useDecorativeTimer() {
|
|
968
|
+
return {
|
|
969
|
+
onVnodeMounted: hideTimer,
|
|
970
|
+
onVnodeUpdated: hideTimer
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
function hideTimer({ el }) {
|
|
974
|
+
el?.querySelector(".v-snackbar__timer [role='progressbar']")?.setAttribute("aria-hidden", "true");
|
|
975
|
+
}
|
|
976
|
+
//#endregion
|
|
518
977
|
//#region src/components/dialogs/toast.vue
|
|
519
978
|
var toast_default = /* @__PURE__ */ defineComponent({
|
|
520
979
|
inheritAttrs: false,
|
|
521
980
|
__name: "toast",
|
|
522
981
|
setup(__props) {
|
|
982
|
+
const decorativeTimer = useDecorativeTimer();
|
|
523
983
|
return (_ctx, _cache) => {
|
|
524
984
|
return openBlock(), createBlock(unref(VSnackbarQueue), {
|
|
525
985
|
modelValue: unref(messages),
|
|
526
986
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(messages) ? messages.value = $event : null),
|
|
987
|
+
"content-props": unref(decorativeTimer),
|
|
527
988
|
"display-strategy": "overflow",
|
|
528
989
|
location: "top end",
|
|
529
990
|
"total-visible": "3",
|
|
@@ -538,111 +999,960 @@ var toast_default = /* @__PURE__ */ defineComponent({
|
|
|
538
999
|
variant: "text"
|
|
539
1000
|
}), null, 16)]),
|
|
540
1001
|
_: 1
|
|
541
|
-
}, 8, ["modelValue"]);
|
|
1002
|
+
}, 8, ["modelValue", "content-props"]);
|
|
542
1003
|
};
|
|
543
1004
|
}
|
|
544
1005
|
});
|
|
545
1006
|
//#endregion
|
|
546
|
-
//#region src/
|
|
547
|
-
|
|
1007
|
+
//#region src/composables/colors/use-vuetify-text-color.ts
|
|
1008
|
+
/**
|
|
1009
|
+
* Maps a color to the theme color to use when it's the text color, e.g. in a ghost button, so it
|
|
1010
|
+
* keeps WCAG AA's contrast on the surface and background.
|
|
1011
|
+
*/
|
|
1012
|
+
function useVuetifyTextColor(color) {
|
|
1013
|
+
return computed(() => colorToVuetifyTextColor[toValue(color) ?? "primary"]);
|
|
1014
|
+
}
|
|
1015
|
+
//#endregion
|
|
1016
|
+
//#region src/composables/button/button.ts
|
|
1017
|
+
const buttonVariantToVuetifyVariant = {
|
|
1018
|
+
primary: "elevated",
|
|
1019
|
+
secondary: "outlined",
|
|
1020
|
+
ghost: "text"
|
|
1021
|
+
};
|
|
1022
|
+
function useButtonVariant(variant) {
|
|
1023
|
+
return computed(() => {
|
|
1024
|
+
const variantValue = toValue(variant);
|
|
1025
|
+
return variantValue ? buttonVariantToVuetifyVariant[variantValue] : buttonVariantToVuetifyVariant.primary;
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* The route the button navigates to, or `undefined` while it's blocked, so a `disabled` or
|
|
1030
|
+
* `loading` button is rendered as a disabled `<button>` instead of a link that the keyboard,
|
|
1031
|
+
* assistive technologies or `element.click()` could still follow.
|
|
1032
|
+
*/
|
|
1033
|
+
function useButtonRoute(route, blocked) {
|
|
1034
|
+
return computed(() => toValue(blocked) ? void 0 : toValue(route));
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* The button's `type`. It doesn't apply to a button with a `route`, which is `undefined` while
|
|
1038
|
+
* rendered as a link, and `"button"` while blocked, so the disabled `<button>` it's rendered as
|
|
1039
|
+
* isn't a submit button.
|
|
1040
|
+
*/
|
|
1041
|
+
function useButtonType(type, route, blocked) {
|
|
1042
|
+
return computed(() => {
|
|
1043
|
+
if (!toValue(route)) return toValue(type) ?? "button";
|
|
1044
|
+
return toValue(blocked) ? "button" : void 0;
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Maps the button's color to Vuetify's. Secondary (outlined) and ghost buttons have no fill, so
|
|
1049
|
+
* their color is the text's, which takes the color's text shade to keep WCAG AA's contrast.
|
|
1050
|
+
*/
|
|
1051
|
+
function useButtonColor(color, variant) {
|
|
1052
|
+
const fillColor = useVuetifyColor(color);
|
|
1053
|
+
const textColor = useVuetifyTextColor(color);
|
|
1054
|
+
return computed(() => {
|
|
1055
|
+
return toValue(variant) === "secondary" || toValue(variant) === "ghost" ? textColor.value : fillColor.value;
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* The button's `form`, or `undefined` when it has a `route`, even while blocked, since `form`
|
|
1060
|
+
* doesn't apply to it.
|
|
1061
|
+
*/
|
|
1062
|
+
function useButtonForm(form, route) {
|
|
1063
|
+
return computed(() => toValue(route) ? void 0 : toValue(form));
|
|
1064
|
+
}
|
|
1065
|
+
//#endregion
|
|
1066
|
+
//#region src/components/button/button.vue
|
|
1067
|
+
var button_default = /* @__PURE__ */ defineComponent({
|
|
548
1068
|
inheritAttrs: false,
|
|
549
|
-
__name: "
|
|
550
|
-
props:
|
|
551
|
-
|
|
1069
|
+
__name: "button",
|
|
1070
|
+
props: {
|
|
1071
|
+
variant: {
|
|
552
1072
|
type: String,
|
|
553
1073
|
required: false
|
|
554
1074
|
},
|
|
555
|
-
|
|
1075
|
+
type: {
|
|
556
1076
|
type: String,
|
|
557
1077
|
required: false
|
|
558
1078
|
},
|
|
559
|
-
|
|
560
|
-
type:
|
|
1079
|
+
color: {
|
|
1080
|
+
type: String,
|
|
561
1081
|
required: false
|
|
562
1082
|
},
|
|
563
|
-
|
|
564
|
-
type:
|
|
1083
|
+
disabled: {
|
|
1084
|
+
type: Boolean,
|
|
565
1085
|
required: false
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
"notificationsOpen": {
|
|
1086
|
+
},
|
|
1087
|
+
loading: {
|
|
569
1088
|
type: Boolean,
|
|
570
|
-
|
|
1089
|
+
required: false
|
|
571
1090
|
},
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
1091
|
+
fullWidth: {
|
|
1092
|
+
type: Boolean,
|
|
1093
|
+
required: false
|
|
1094
|
+
},
|
|
1095
|
+
form: {
|
|
1096
|
+
type: String,
|
|
1097
|
+
required: false
|
|
1098
|
+
},
|
|
1099
|
+
route: {
|
|
1100
|
+
type: null,
|
|
1101
|
+
required: false
|
|
1102
|
+
},
|
|
1103
|
+
dataTestid: {
|
|
1104
|
+
type: String,
|
|
1105
|
+
required: false
|
|
1106
|
+
}
|
|
1107
|
+
},
|
|
1108
|
+
emits: ["click"],
|
|
1109
|
+
setup(__props, { emit: __emit }) {
|
|
1110
|
+
const props = __props;
|
|
1111
|
+
const emit = __emit;
|
|
1112
|
+
const variant = useButtonVariant(() => props.variant);
|
|
1113
|
+
const color = useButtonColor(() => props.color, () => props.variant);
|
|
1114
|
+
const widthClass = useFullWidth(() => props.fullWidth);
|
|
1115
|
+
const isDisabled = computed(() => props.disabled || props.loading);
|
|
1116
|
+
const routeProps = useRouteProps(useButtonRoute(() => props.route, isDisabled));
|
|
1117
|
+
const type = useButtonType(() => props.type, () => props.route, isDisabled);
|
|
1118
|
+
const form = useButtonForm(() => props.form, () => props.route);
|
|
1119
|
+
function onClick(event) {
|
|
1120
|
+
emit("click", event);
|
|
1121
|
+
}
|
|
1122
|
+
return (_ctx, _cache) => {
|
|
1123
|
+
return openBlock(), createBlock(unref(VBtn), mergeProps({
|
|
1124
|
+
disabled: isDisabled.value,
|
|
1125
|
+
loading: props.loading,
|
|
1126
|
+
"data-testid": props.dataTestid,
|
|
1127
|
+
variant: unref(variant),
|
|
1128
|
+
color: unref(color),
|
|
1129
|
+
type: unref(type),
|
|
1130
|
+
form: unref(form),
|
|
1131
|
+
class: unref(widthClass)
|
|
1132
|
+
}, unref(routeProps), {
|
|
1133
|
+
active: false,
|
|
1134
|
+
onClick
|
|
1135
|
+
}), {
|
|
1136
|
+
loader: withCtx(() => [createVNode(unref(VProgressCircular), {
|
|
1137
|
+
indeterminate: "",
|
|
1138
|
+
width: "2",
|
|
1139
|
+
"aria-hidden": "true"
|
|
1140
|
+
})]),
|
|
1141
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
1142
|
+
_: 3
|
|
1143
|
+
}, 16, [
|
|
1144
|
+
"disabled",
|
|
1145
|
+
"loading",
|
|
1146
|
+
"data-testid",
|
|
1147
|
+
"variant",
|
|
1148
|
+
"color",
|
|
1149
|
+
"type",
|
|
1150
|
+
"form",
|
|
1151
|
+
"class"
|
|
1152
|
+
]);
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
//#endregion
|
|
1157
|
+
//#region src/components/dialogs/dialog/dialog-host-key.ts
|
|
1158
|
+
/**
|
|
1159
|
+
* Injection key of {@link DialogHostContext}, provided by the internal `DialogHost`. Besides the
|
|
1160
|
+
* role, the blocking while an action runs and the end of the leave transitions, it makes the
|
|
1161
|
+
* dialog's content describe the dialog (`aria-describedby`), since `DialogHost` only displays text
|
|
1162
|
+
* messages. Not part of the public API.
|
|
1163
|
+
*/
|
|
1164
|
+
const dialogHostKey = Symbol("dialog-host");
|
|
1165
|
+
//#endregion
|
|
1166
|
+
//#region src/composables/dialogs/use-dialog-focus.ts
|
|
1167
|
+
/** Focus state of the closed dialogs, by their card, for the dialogs opened from them. */
|
|
1168
|
+
const closedDialogs = /* @__PURE__ */ new WeakMap();
|
|
1169
|
+
/** Elements that can get the focus when the dialog opens. The first one in the dialog gets it. */
|
|
1170
|
+
const focusable = [
|
|
1171
|
+
"button:not([disabled])",
|
|
1172
|
+
"[href]",
|
|
1173
|
+
"input:not([disabled]):not([type=\"hidden\"])",
|
|
1174
|
+
"select:not([disabled])",
|
|
1175
|
+
"textarea:not([disabled])",
|
|
1176
|
+
"[tabindex]:not([tabindex=\"-1\"])"
|
|
1177
|
+
].join(", ");
|
|
1178
|
+
/**
|
|
1179
|
+
* Moves the focus into a dialog once it opens and back to where it was when it closes, including
|
|
1180
|
+
* when dialogs are opened from others. Internal to the dialog components.
|
|
1181
|
+
* @param isActive - Whether the dialog is displayed. It turns `false` as soon as the dialog starts
|
|
1182
|
+
* leaving, so the focus returns right away.
|
|
1183
|
+
* @param card - The dialog's card, marked with `data-u-dialog`, or nothing while it isn't rendered.
|
|
1184
|
+
* @returns `focusFirstElement`, to call once the dialog finishes entering.
|
|
1185
|
+
*/
|
|
1186
|
+
function useDialogFocus(isActive, card) {
|
|
1187
|
+
/** Focus state of this dialog, shared with the dialogs opened from it. */
|
|
1188
|
+
const focusState = {
|
|
1189
|
+
isOpen: () => toValue(isActive),
|
|
1190
|
+
origin: null
|
|
1191
|
+
};
|
|
1192
|
+
onMounted(() => onActiveChange(toValue(isActive)));
|
|
1193
|
+
watch(() => toValue(isActive), onActiveChange);
|
|
1194
|
+
function onActiveChange(active) {
|
|
1195
|
+
if (active) {
|
|
1196
|
+
focusState.origin = document.activeElement;
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
const element = toValue(card);
|
|
1200
|
+
if (element) closedDialogs.set(element, focusState);
|
|
1201
|
+
const target = findFocusTarget(focusState.origin);
|
|
1202
|
+
if (hasFocus(element) && (target instanceof HTMLElement || target instanceof SVGElement)) target.focus();
|
|
1203
|
+
}
|
|
1204
|
+
function focusFirstElement() {
|
|
1205
|
+
toValue(card)?.querySelector(focusable)?.focus();
|
|
1206
|
+
}
|
|
1207
|
+
return { focusFirstElement };
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Element that gets the focus back when a dialog closes: its origin or, when the origin is inside
|
|
1211
|
+
* another dialog that closed meanwhile (and can't get the focus anymore), that dialog's.
|
|
1212
|
+
*/
|
|
1213
|
+
function findFocusTarget(origin) {
|
|
1214
|
+
const card = origin?.closest("[data-u-dialog]");
|
|
1215
|
+
const owner = card && closedDialogs.get(card);
|
|
1216
|
+
return owner && !owner.isOpen() ? findFocusTarget(owner.origin) : origin;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Whether the focus is on the dialog or lost (on nothing or on the page's body), so it can move.
|
|
1220
|
+
* Otherwise it moved elsewhere, e.g. to another dialog opened over this one.
|
|
1221
|
+
*/
|
|
1222
|
+
function hasFocus(card) {
|
|
1223
|
+
const active = document.activeElement;
|
|
1224
|
+
if (!active) return true;
|
|
1225
|
+
return !!card && (card.contains(active) || active.contains(card));
|
|
1226
|
+
}
|
|
1227
|
+
//#endregion
|
|
1228
|
+
//#region src/composables/dialogs/use-dialog-loading.ts
|
|
1229
|
+
/**
|
|
1230
|
+
* Tells whether any action of a dialog is loading, during which the user can't close it with
|
|
1231
|
+
* `Esc`, a click outside or the browser's back button. Internal to the dialog components.
|
|
1232
|
+
* @param actions - The dialog's actions, if any.
|
|
1233
|
+
* @returns `isLoading`, which is `true` while at least one action is `loading`.
|
|
1234
|
+
*/
|
|
1235
|
+
function useDialogLoading(actions) {
|
|
1236
|
+
return { isLoading: computed(() => toValue(actions)?.some((action) => action.loading) ?? false) };
|
|
1237
|
+
}
|
|
1238
|
+
//#endregion
|
|
1239
|
+
//#region src/components/dialogs/dialog/dialog.vue?vue&type=script&setup=true&lang.ts
|
|
1240
|
+
const _hoisted_1$1 = ["id"];
|
|
1241
|
+
//#endregion
|
|
1242
|
+
//#region src/components/dialogs/dialog/dialog.vue
|
|
1243
|
+
var dialog_default = /* @__PURE__ */ defineComponent({
|
|
1244
|
+
inheritAttrs: false,
|
|
1245
|
+
__name: "dialog",
|
|
1246
|
+
props: /*@__PURE__*/ mergeModels({
|
|
1247
|
+
title: {
|
|
1248
|
+
type: String,
|
|
1249
|
+
required: false
|
|
1250
|
+
},
|
|
1251
|
+
actions: {
|
|
1252
|
+
type: Array,
|
|
1253
|
+
required: false
|
|
1254
|
+
},
|
|
1255
|
+
size: {
|
|
1256
|
+
type: String,
|
|
1257
|
+
required: false
|
|
1258
|
+
},
|
|
1259
|
+
dataTestid: {
|
|
1260
|
+
type: String,
|
|
1261
|
+
required: false
|
|
1262
|
+
}
|
|
1263
|
+
}, {
|
|
1264
|
+
"modelValue": {
|
|
1265
|
+
type: Boolean,
|
|
1266
|
+
default: false
|
|
1267
|
+
},
|
|
1268
|
+
"modelModifiers": {}
|
|
1269
|
+
}),
|
|
1270
|
+
emits: ["update:modelValue"],
|
|
1271
|
+
setup(__props) {
|
|
1272
|
+
const props = __props;
|
|
1273
|
+
const model = useModel(__props, "modelValue");
|
|
1274
|
+
const maxWidths = {
|
|
1275
|
+
small: 400,
|
|
1276
|
+
medium: 560,
|
|
1277
|
+
large: 800
|
|
1278
|
+
};
|
|
1279
|
+
const host = inject(dialogHostKey, null);
|
|
1280
|
+
const contentId = useId();
|
|
1281
|
+
const card = useTemplateRef("card");
|
|
1282
|
+
const title = computed(() => props.title || void 0);
|
|
1283
|
+
const maxWidth = computed(() => maxWidths[props.size ?? "medium"]);
|
|
1284
|
+
const role = computed(() => host?.role() ?? "dialog");
|
|
1285
|
+
/**
|
|
1286
|
+
* Whether the dialog is displayed. It follows `v-model`, but turns `false` as soon as the user
|
|
1287
|
+
* closes the dialog, while `v-model` only does once the leave transition ends.
|
|
1288
|
+
*/
|
|
1289
|
+
const isActive = ref(model.value);
|
|
1290
|
+
watch(model, (value) => isActive.value = value);
|
|
1291
|
+
const { isLoading } = useDialogLoading(() => props.actions);
|
|
1292
|
+
const { focusFirstElement } = useDialogFocus(() => isActive.value, () => card.value?.$el);
|
|
1293
|
+
function onAfterLeave() {
|
|
1294
|
+
model.value = false;
|
|
1295
|
+
host?.afterLeave();
|
|
1296
|
+
}
|
|
1297
|
+
return (_ctx, _cache) => {
|
|
1298
|
+
return openBlock(), createBlock(unref(VDialog), {
|
|
1299
|
+
modelValue: isActive.value,
|
|
1300
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isActive.value = $event),
|
|
1301
|
+
persistent: unref(isLoading),
|
|
1302
|
+
"max-width": maxWidth.value,
|
|
1303
|
+
role: role.value,
|
|
1304
|
+
"aria-label": title.value,
|
|
1305
|
+
"aria-labelledby": title.value ? void 0 : unref(contentId),
|
|
1306
|
+
"aria-describedby": unref(host) ? unref(contentId) : void 0,
|
|
1307
|
+
"data-testid": props.dataTestid,
|
|
1308
|
+
scrollable: "",
|
|
1309
|
+
onAfterEnter: unref(focusFirstElement),
|
|
1310
|
+
onAfterLeave
|
|
1311
|
+
}, {
|
|
1312
|
+
default: withCtx(() => [createCommentVNode(" `data-u-dialog` identifies the card to the dialogs opened from this one "), createVNode(unref(VCard), {
|
|
1313
|
+
ref_key: "card",
|
|
1314
|
+
ref: card,
|
|
1315
|
+
title: title.value,
|
|
1316
|
+
"data-u-dialog": ""
|
|
1317
|
+
}, createSlots({
|
|
1318
|
+
text: withCtx(() => [createElementVNode("div", { id: unref(contentId) }, [renderSlot(_ctx.$slots, "default")], 8, _hoisted_1$1)]),
|
|
1319
|
+
_: 2
|
|
1320
|
+
}, [props.actions?.length ? {
|
|
1321
|
+
name: "actions",
|
|
1322
|
+
fn: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.actions, ({ label, onClick, ...action }, index) => {
|
|
1323
|
+
return openBlock(), createBlock(button_default, mergeProps({ key: index }, { ref_for: true }, action, { onClick }), {
|
|
1324
|
+
default: withCtx(() => [createTextVNode(toDisplayString(label), 1)]),
|
|
1325
|
+
_: 2
|
|
1326
|
+
}, 1040, ["onClick"]);
|
|
1327
|
+
}), 128))]),
|
|
1328
|
+
key: "0"
|
|
1329
|
+
} : void 0]), 1032, ["title"])]),
|
|
1330
|
+
_: 3
|
|
1331
|
+
}, 8, [
|
|
1332
|
+
"modelValue",
|
|
1333
|
+
"persistent",
|
|
1334
|
+
"max-width",
|
|
1335
|
+
"role",
|
|
1336
|
+
"aria-label",
|
|
1337
|
+
"aria-labelledby",
|
|
1338
|
+
"aria-describedby",
|
|
1339
|
+
"data-testid",
|
|
1340
|
+
"onAfterEnter"
|
|
1341
|
+
]);
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
//#endregion
|
|
1346
|
+
//#region src/components/dialogs/dialog-host.vue
|
|
1347
|
+
var dialog_host_default = /* @__PURE__ */ defineComponent({
|
|
1348
|
+
inheritAttrs: false,
|
|
1349
|
+
__name: "dialog-host",
|
|
1350
|
+
setup(__props) {
|
|
1351
|
+
const owner = Symbol("dialog");
|
|
1352
|
+
const isOwner = computed(() => owners$1.value[0] === owner);
|
|
1353
|
+
const next = computed(() => isOwner.value ? requests.value[0] : void 0);
|
|
1354
|
+
/** Dialog on display, kept until its leave transition ends. */
|
|
1355
|
+
const request = shallowRef();
|
|
1356
|
+
/**
|
|
1357
|
+
* Changes for every dialog on display, so each one gets its own `Dialog`, open from the start.
|
|
1358
|
+
* The next dialog can then open right after a dismissed one, whose `v-model` stays `true`.
|
|
1359
|
+
*/
|
|
1360
|
+
const key = shallowRef();
|
|
1361
|
+
const isOpen = ref(false);
|
|
1362
|
+
const selected = shallowRef();
|
|
1363
|
+
const isRunning = computed(() => selected.value !== void 0);
|
|
1364
|
+
const actions = computed(() => {
|
|
1365
|
+
const current = request.value;
|
|
1366
|
+
if (!current) return void 0;
|
|
1367
|
+
return current.actions.map((action) => ({
|
|
1368
|
+
label: action.text,
|
|
1369
|
+
variant: action.variant,
|
|
1370
|
+
color: action.color,
|
|
1371
|
+
loading: selected.value === action,
|
|
1372
|
+
disabled: isRunning.value && selected.value !== action,
|
|
1373
|
+
onClick: () => void select(current, action)
|
|
1374
|
+
}));
|
|
1375
|
+
});
|
|
1376
|
+
provide(dialogHostKey, {
|
|
1377
|
+
role: () => request.value?.role,
|
|
1378
|
+
afterLeave: showNext
|
|
1379
|
+
});
|
|
1380
|
+
onMounted(() => owners$1.value.push(owner));
|
|
1381
|
+
onUnmounted(() => owners$1.value = owners$1.value.filter((item) => item !== owner));
|
|
1382
|
+
watch(next, show);
|
|
1383
|
+
function show() {
|
|
1384
|
+
if (request.value) return;
|
|
1385
|
+
request.value = next.value;
|
|
1386
|
+
key.value = Symbol();
|
|
1387
|
+
isOpen.value = next.value !== void 0;
|
|
1388
|
+
}
|
|
1389
|
+
function showNext() {
|
|
1390
|
+
request.value = void 0;
|
|
1391
|
+
show();
|
|
1392
|
+
}
|
|
1393
|
+
async function select(current, action) {
|
|
1394
|
+
requests.value.shift();
|
|
1395
|
+
selected.value = action;
|
|
1396
|
+
await current.select(action?.value);
|
|
1397
|
+
selected.value = void 0;
|
|
1398
|
+
if (action) isOpen.value = false;
|
|
1399
|
+
}
|
|
1400
|
+
/** Called once a dialog closed by the user leaves, right before the next one is displayed. */
|
|
1401
|
+
function dismiss() {
|
|
1402
|
+
if (request.value) select(request.value);
|
|
1403
|
+
}
|
|
1404
|
+
return (_ctx, _cache) => {
|
|
1405
|
+
return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" PascalCase, since `<dialog>` in the template would render the native HTML element instead "), (openBlock(), createBlock(dialog_default, {
|
|
1406
|
+
key: key.value,
|
|
1407
|
+
"model-value": isOpen.value,
|
|
1408
|
+
title: request.value?.title,
|
|
1409
|
+
actions: actions.value,
|
|
1410
|
+
"onUpdate:modelValue": dismiss
|
|
1411
|
+
}, {
|
|
1412
|
+
default: withCtx(() => [createTextVNode(toDisplayString(request.value?.message), 1)]),
|
|
1413
|
+
_: 1
|
|
1414
|
+
}, 8, [
|
|
1415
|
+
"model-value",
|
|
1416
|
+
"title",
|
|
1417
|
+
"actions"
|
|
1418
|
+
]))], 2112);
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
});
|
|
1422
|
+
//#endregion
|
|
1423
|
+
//#region src/composables/colors/use-vuetify-background-color.ts
|
|
1424
|
+
/**
|
|
1425
|
+
* Vuetify's utility class that applies a color to the background of an element, and the color the
|
|
1426
|
+
* theme pairs with it to its text, for props that color a background. No class when the color is
|
|
1427
|
+
* omitted, so the element keeps its default background.
|
|
1428
|
+
*/
|
|
1429
|
+
function useVuetifyBackgroundColor(color) {
|
|
1430
|
+
return computed(() => {
|
|
1431
|
+
const colorValue = toValue(color);
|
|
1432
|
+
return colorValue ? `bg-${colorToVuetifyColor[colorValue]}` : void 0;
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
//#endregion
|
|
1436
|
+
//#region src/components/main/main.vue
|
|
1437
|
+
var main_default = /* @__PURE__ */ defineComponent({
|
|
1438
|
+
inheritAttrs: false,
|
|
1439
|
+
__name: "main",
|
|
1440
|
+
props: /*@__PURE__*/ mergeModels({
|
|
1441
|
+
dataTestid: {
|
|
1442
|
+
type: String,
|
|
1443
|
+
required: false
|
|
1444
|
+
},
|
|
1445
|
+
logo: {
|
|
1446
|
+
type: String,
|
|
1447
|
+
required: false
|
|
1448
|
+
},
|
|
1449
|
+
appBar: {
|
|
1450
|
+
type: Object,
|
|
1451
|
+
required: false
|
|
1452
|
+
},
|
|
1453
|
+
navigationMenu: {
|
|
1454
|
+
type: Object,
|
|
1455
|
+
required: false
|
|
1456
|
+
},
|
|
1457
|
+
footer: {
|
|
1458
|
+
type: Object,
|
|
1459
|
+
required: false
|
|
1460
|
+
},
|
|
1461
|
+
loading: {
|
|
1462
|
+
type: Boolean,
|
|
1463
|
+
required: false
|
|
1464
|
+
},
|
|
1465
|
+
background: {
|
|
1466
|
+
type: String,
|
|
1467
|
+
required: false
|
|
1468
|
+
}
|
|
1469
|
+
}, {
|
|
1470
|
+
"notificationsOpen": {
|
|
1471
|
+
type: Boolean,
|
|
1472
|
+
default: false
|
|
1473
|
+
},
|
|
1474
|
+
"notificationsOpenModifiers": {}
|
|
1475
|
+
}),
|
|
1476
|
+
emits: ["update:notificationsOpen"],
|
|
1477
|
+
setup(__props) {
|
|
1478
|
+
const props = __props;
|
|
1479
|
+
const appBarProps = computed(() => props.appBar ? {
|
|
1480
|
+
...props.appBar,
|
|
1481
|
+
logo: props.logo,
|
|
1482
|
+
navigation: props.navigationMenu !== void 0,
|
|
1483
|
+
loading: props.loading
|
|
1484
|
+
} : void 0);
|
|
1485
|
+
const backgroundClass = useVuetifyBackgroundColor(() => props.background);
|
|
1486
|
+
const notificationsOpen = useModel(__props, "notificationsOpen");
|
|
583
1487
|
const navigationMenuOpen = ref(false);
|
|
584
1488
|
return (_ctx, _cache) => {
|
|
585
|
-
return openBlock(), createBlock(unref(VApp), {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
"
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
1489
|
+
return openBlock(), createBlock(unref(VApp), {
|
|
1490
|
+
class: normalizeClass(unref(backgroundClass)),
|
|
1491
|
+
"data-testid": props.dataTestid
|
|
1492
|
+
}, {
|
|
1493
|
+
default: withCtx(() => [
|
|
1494
|
+
appBarProps.value ? (openBlock(), createBlock(app_bar_default, mergeProps({ key: 0 }, appBarProps.value, {
|
|
1495
|
+
"notifications-open": notificationsOpen.value,
|
|
1496
|
+
"onUpdate:notificationsOpen": _cache[0] || (_cache[0] = ($event) => notificationsOpen.value = $event),
|
|
1497
|
+
"navigation-open": navigationMenuOpen.value,
|
|
1498
|
+
"onUpdate:navigationOpen": _cache[1] || (_cache[1] = ($event) => navigationMenuOpen.value = $event)
|
|
1499
|
+
}), null, 16, ["notifications-open", "navigation-open"])) : createCommentVNode("v-if", true),
|
|
1500
|
+
props.navigationMenu ? (openBlock(), createBlock(navigation_menu_default, mergeProps({ key: 1 }, props.navigationMenu, {
|
|
1501
|
+
modelValue: navigationMenuOpen.value,
|
|
1502
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => navigationMenuOpen.value = $event),
|
|
1503
|
+
loading: props.loading
|
|
1504
|
+
}), null, 16, ["modelValue", "loading"])) : createCommentVNode("v-if", true),
|
|
1505
|
+
createVNode(unref(VMain), null, {
|
|
1506
|
+
default: withCtx(() => [createCommentVNode(" Full height, so the page's rows and containers with `fullHeight` have a height to fill "), createVNode(container_default, { "full-height": "" }, {
|
|
1507
|
+
default: withCtx(() => [
|
|
1508
|
+
renderSlot(_ctx.$slots, "default"),
|
|
1509
|
+
createVNode(toast_default),
|
|
1510
|
+
createVNode(dialog_host_default)
|
|
1511
|
+
]),
|
|
1512
|
+
_: 3
|
|
1513
|
+
})]),
|
|
1514
|
+
_: 3
|
|
1515
|
+
}),
|
|
1516
|
+
props.footer?.description ? (openBlock(), createBlock(footer_default, normalizeProps(mergeProps({ key: 2 }, props.footer)), null, 16)) : createCommentVNode("v-if", true)
|
|
1517
|
+
]),
|
|
1518
|
+
_: 3
|
|
1519
|
+
}, 8, ["class", "data-testid"]);
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
//#endregion
|
|
1524
|
+
//#region src/components/icon-button/icon-button.vue
|
|
1525
|
+
var icon_button_default = /* @__PURE__ */ defineComponent({
|
|
1526
|
+
inheritAttrs: false,
|
|
1527
|
+
__name: "icon-button",
|
|
1528
|
+
props: {
|
|
1529
|
+
icon: {
|
|
1530
|
+
type: null,
|
|
1531
|
+
required: true
|
|
1532
|
+
},
|
|
1533
|
+
label: {
|
|
1534
|
+
type: String,
|
|
1535
|
+
required: true
|
|
1536
|
+
},
|
|
1537
|
+
color: {
|
|
1538
|
+
type: String,
|
|
1539
|
+
required: false
|
|
1540
|
+
},
|
|
1541
|
+
dataTestid: {
|
|
1542
|
+
type: String,
|
|
1543
|
+
required: false
|
|
1544
|
+
}
|
|
1545
|
+
},
|
|
1546
|
+
emits: ["click"],
|
|
1547
|
+
setup(__props, { emit: __emit }) {
|
|
1548
|
+
const props = __props;
|
|
1549
|
+
const emit = __emit;
|
|
1550
|
+
const color = useVuetifyColor(() => props.color);
|
|
1551
|
+
function onClick(event) {
|
|
1552
|
+
emit("click", event);
|
|
1553
|
+
}
|
|
1554
|
+
return (_ctx, _cache) => {
|
|
1555
|
+
return openBlock(), createBlock(unref(VIconBtn), {
|
|
1556
|
+
icon: unref(icons)[props.icon],
|
|
1557
|
+
"aria-label": props.label,
|
|
1558
|
+
color: unref(color),
|
|
1559
|
+
"data-testid": props.dataTestid,
|
|
1560
|
+
onClick
|
|
1561
|
+
}, null, 8, [
|
|
1562
|
+
"icon",
|
|
1563
|
+
"aria-label",
|
|
1564
|
+
"color",
|
|
1565
|
+
"data-testid"
|
|
1566
|
+
]);
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
//#endregion
|
|
1571
|
+
//#region src/composables/inputs/autocomplete-field.ts
|
|
1572
|
+
function createAutocompleteRequiredRule(options) {
|
|
1573
|
+
return createRequiredRule(options.required, (value) => {
|
|
1574
|
+
if (options.multiple) return (Array.isArray(value) ? value : []).length > 0;
|
|
1575
|
+
return !isEmpty(String(value ?? ""));
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
function useAutocompleteRules(options) {
|
|
1579
|
+
return computed(() => {
|
|
1580
|
+
const opts = toValue(options);
|
|
1581
|
+
const rules = [];
|
|
1582
|
+
if (opts.required) rules.push(createAutocompleteRequiredRule(opts));
|
|
1583
|
+
return rules;
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Menu props that keep the combobox semantics only on the field's `<input>`.
|
|
1588
|
+
*
|
|
1589
|
+
* The field's container gets `role="combobox"` from the field, which passes its
|
|
1590
|
+
* role to both the container and the input, and the menu's ARIA attributes,
|
|
1591
|
+
* since it's the menu's activator. Without these props, the container is
|
|
1592
|
+
* exposed as an unnamed combobox wrapping the input. The menu removes from its
|
|
1593
|
+
* activator the attributes set to `undefined`, role included, and the field
|
|
1594
|
+
* never sets that role again, since its value doesn't change. The input keeps
|
|
1595
|
+
* its role, label, `aria-expanded` and `aria-controls`. This only happens in
|
|
1596
|
+
* the browser: server-rendered HTML still has the role on the container.
|
|
1597
|
+
*/
|
|
1598
|
+
const autocompleteFieldMenuProps = { activatorProps: {
|
|
1599
|
+
role: void 0,
|
|
1600
|
+
"aria-haspopup": void 0,
|
|
1601
|
+
"aria-expanded": void 0,
|
|
1602
|
+
"aria-controls": void 0,
|
|
1603
|
+
"aria-owns": void 0
|
|
1604
|
+
} };
|
|
1605
|
+
//#endregion
|
|
1606
|
+
//#region src/components/inputs/autocomplete-field/autocomplete-field.vue
|
|
1607
|
+
var autocomplete_field_default = /* @__PURE__ */ defineComponent({
|
|
1608
|
+
inheritAttrs: false,
|
|
1609
|
+
__name: "autocomplete-field",
|
|
1610
|
+
props: /*@__PURE__*/ mergeModels({
|
|
1611
|
+
variant: {
|
|
1612
|
+
type: String,
|
|
1613
|
+
required: false,
|
|
1614
|
+
default: "primary"
|
|
1615
|
+
},
|
|
1616
|
+
disabled: {
|
|
1617
|
+
type: Boolean,
|
|
1618
|
+
required: false
|
|
1619
|
+
},
|
|
1620
|
+
loading: {
|
|
1621
|
+
type: Boolean,
|
|
1622
|
+
required: false
|
|
1623
|
+
},
|
|
1624
|
+
clearable: {
|
|
1625
|
+
type: Boolean,
|
|
1626
|
+
required: false
|
|
1627
|
+
},
|
|
1628
|
+
readonly: {
|
|
1629
|
+
type: Boolean,
|
|
1630
|
+
required: false
|
|
1631
|
+
},
|
|
1632
|
+
required: {
|
|
1633
|
+
type: Boolean,
|
|
1634
|
+
required: false
|
|
1635
|
+
},
|
|
1636
|
+
label: {
|
|
1637
|
+
type: String,
|
|
1638
|
+
required: false
|
|
1639
|
+
},
|
|
1640
|
+
placeholder: {
|
|
1641
|
+
type: String,
|
|
1642
|
+
required: false
|
|
1643
|
+
},
|
|
1644
|
+
hint: {
|
|
1645
|
+
type: String,
|
|
1646
|
+
required: false
|
|
1647
|
+
},
|
|
1648
|
+
dataTestid: {
|
|
1649
|
+
type: String,
|
|
1650
|
+
required: false
|
|
1651
|
+
},
|
|
1652
|
+
strict: {
|
|
1653
|
+
type: Boolean,
|
|
1654
|
+
required: false
|
|
1655
|
+
},
|
|
1656
|
+
multiple: {
|
|
1657
|
+
type: null,
|
|
1658
|
+
required: false
|
|
1659
|
+
},
|
|
1660
|
+
items: {
|
|
1661
|
+
type: Array,
|
|
1662
|
+
required: false,
|
|
1663
|
+
default: () => []
|
|
1664
|
+
}
|
|
1665
|
+
}, {
|
|
1666
|
+
"modelValue": { type: null },
|
|
1667
|
+
"modelModifiers": {}
|
|
1668
|
+
}),
|
|
1669
|
+
emits: ["update:modelValue"],
|
|
1670
|
+
setup(__props) {
|
|
1671
|
+
const modelValue = useModel(__props, "modelValue");
|
|
1672
|
+
const props = __props;
|
|
1673
|
+
const vuetifyVariant = useTextFieldVariant(() => props.variant);
|
|
1674
|
+
const isMultiple = computed(() => props.multiple ?? false);
|
|
1675
|
+
const rules = useAutocompleteRules(() => ({
|
|
1676
|
+
required: props.required,
|
|
1677
|
+
multiple: isMultiple.value
|
|
1678
|
+
}));
|
|
1679
|
+
const vuetifyComponent = computed(() => props.strict ? VAutocomplete : VCombobox);
|
|
1680
|
+
const search = ref("");
|
|
1681
|
+
const maskClass = computed(() => props.strict ? "v-autocomplete__mask" : "v-combobox__mask");
|
|
1682
|
+
const normalizedItems = computed(() => {
|
|
1683
|
+
return props.items.map((item) => {
|
|
1684
|
+
if (typeof item === "string") return {
|
|
1685
|
+
label: item,
|
|
1686
|
+
value: item
|
|
1687
|
+
};
|
|
1688
|
+
return item;
|
|
1689
|
+
});
|
|
1690
|
+
});
|
|
1691
|
+
return (_ctx, _cache) => {
|
|
1692
|
+
return openBlock(), createBlock(resolveDynamicComponent(vuetifyComponent.value), {
|
|
1693
|
+
modelValue: modelValue.value,
|
|
1694
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
|
|
1695
|
+
search: search.value,
|
|
1696
|
+
"onUpdate:search": _cache[1] || (_cache[1] = ($event) => search.value = $event),
|
|
1697
|
+
variant: unref(vuetifyVariant),
|
|
1698
|
+
items: normalizedItems.value,
|
|
1699
|
+
"data-testid": props.dataTestid,
|
|
1700
|
+
disabled: props.disabled,
|
|
1701
|
+
loading: props.loading,
|
|
1702
|
+
"aria-busy": props.loading || void 0,
|
|
1703
|
+
readonly: props.readonly,
|
|
1704
|
+
label: props.label,
|
|
1705
|
+
placeholder: props.placeholder,
|
|
1706
|
+
hint: props.hint,
|
|
1707
|
+
clearable: props.clearable,
|
|
1708
|
+
multiple: isMultiple.value,
|
|
1709
|
+
"item-title": "label",
|
|
1710
|
+
rules: unref(rules),
|
|
1711
|
+
"menu-props": unref(autocompleteFieldMenuProps),
|
|
1712
|
+
chips: ""
|
|
1713
|
+
}, createSlots({
|
|
1714
|
+
loader: withCtx((loader) => [createVNode(field_loader_default, normalizeProps(guardReactiveProps(loader)), null, 16)]),
|
|
1715
|
+
_: 2
|
|
1716
|
+
}, [isMultiple.value ? {
|
|
1717
|
+
name: "item",
|
|
1718
|
+
fn: withCtx(({ props: itemProps }) => [createVNode(unref(VListItem), normalizeProps(guardReactiveProps(itemProps)), {
|
|
1719
|
+
prepend: withCtx(({ isSelected }) => [createVNode(unref(VCheckboxBtn), {
|
|
1720
|
+
"model-value": isSelected,
|
|
1721
|
+
ripple: false,
|
|
1722
|
+
inert: "",
|
|
1723
|
+
"aria-hidden": "true"
|
|
1724
|
+
}, null, 8, ["model-value"])]),
|
|
1725
|
+
title: withCtx(() => [createVNode(unref(VHighlight), {
|
|
1726
|
+
text: itemProps.title,
|
|
1727
|
+
query: search.value,
|
|
1728
|
+
"match-all": "",
|
|
1729
|
+
"ignore-case": "",
|
|
1730
|
+
"mark-class": maskClass.value
|
|
1731
|
+
}, null, 8, [
|
|
1732
|
+
"text",
|
|
1733
|
+
"query",
|
|
1734
|
+
"mark-class"
|
|
1735
|
+
])]),
|
|
1736
|
+
_: 2
|
|
1737
|
+
}, 1040)]),
|
|
1738
|
+
key: "0"
|
|
1739
|
+
} : void 0]), 1064, [
|
|
1740
|
+
"modelValue",
|
|
1741
|
+
"search",
|
|
1742
|
+
"variant",
|
|
1743
|
+
"items",
|
|
1744
|
+
"data-testid",
|
|
1745
|
+
"disabled",
|
|
1746
|
+
"loading",
|
|
1747
|
+
"aria-busy",
|
|
1748
|
+
"readonly",
|
|
1749
|
+
"label",
|
|
1750
|
+
"placeholder",
|
|
1751
|
+
"hint",
|
|
1752
|
+
"clearable",
|
|
1753
|
+
"multiple",
|
|
1754
|
+
"rules",
|
|
1755
|
+
"menu-props"
|
|
1756
|
+
]);
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
});
|
|
1760
|
+
//#endregion
|
|
1761
|
+
//#region src/components/inputs/checkbox/checkbox.vue
|
|
1762
|
+
var checkbox_default = /* @__PURE__ */ defineComponent({
|
|
1763
|
+
inheritAttrs: false,
|
|
1764
|
+
__name: "checkbox",
|
|
1765
|
+
props: /*@__PURE__*/ mergeModels({
|
|
1766
|
+
label: {
|
|
1767
|
+
type: String,
|
|
1768
|
+
required: false
|
|
1769
|
+
},
|
|
1770
|
+
disabled: {
|
|
1771
|
+
type: Boolean,
|
|
1772
|
+
required: false
|
|
1773
|
+
},
|
|
1774
|
+
readonly: {
|
|
1775
|
+
type: Boolean,
|
|
1776
|
+
required: false
|
|
1777
|
+
},
|
|
1778
|
+
trueValue: {
|
|
1779
|
+
type: Boolean,
|
|
1780
|
+
required: false,
|
|
1781
|
+
skipCheck: true,
|
|
1782
|
+
default: true
|
|
1783
|
+
},
|
|
1784
|
+
falseValue: {
|
|
1785
|
+
type: Boolean,
|
|
1786
|
+
required: false,
|
|
1787
|
+
skipCheck: true,
|
|
1788
|
+
default: false
|
|
1789
|
+
},
|
|
1790
|
+
dataTestid: {
|
|
1791
|
+
type: String,
|
|
1792
|
+
required: false
|
|
1793
|
+
}
|
|
1794
|
+
}, {
|
|
1795
|
+
"modelValue": {
|
|
1796
|
+
type: null,
|
|
1797
|
+
default: (props) => props.falseValue
|
|
1798
|
+
},
|
|
1799
|
+
"modelModifiers": {}
|
|
1800
|
+
}),
|
|
1801
|
+
emits: ["update:modelValue"],
|
|
1802
|
+
setup(__props) {
|
|
1803
|
+
const props = __props;
|
|
1804
|
+
const modelValue = useModel(__props, "modelValue");
|
|
1805
|
+
return (_ctx, _cache) => {
|
|
1806
|
+
return openBlock(), createBlock(unref(VCheckboxBtn), {
|
|
1807
|
+
modelValue: modelValue.value,
|
|
1808
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
|
|
1809
|
+
"true-value": props.trueValue,
|
|
1810
|
+
"false-value": props.falseValue,
|
|
1811
|
+
"data-testid": props.dataTestid,
|
|
1812
|
+
disabled: props.disabled,
|
|
1813
|
+
readonly: props.readonly,
|
|
1814
|
+
label: props.label,
|
|
1815
|
+
class: "flex-0-1",
|
|
1816
|
+
"hide-details": ""
|
|
1817
|
+
}, null, 8, [
|
|
1818
|
+
"modelValue",
|
|
1819
|
+
"true-value",
|
|
1820
|
+
"false-value",
|
|
1821
|
+
"data-testid",
|
|
1822
|
+
"disabled",
|
|
1823
|
+
"readonly",
|
|
1824
|
+
"label"
|
|
1825
|
+
]);
|
|
607
1826
|
};
|
|
608
1827
|
}
|
|
609
1828
|
});
|
|
610
1829
|
//#endregion
|
|
611
|
-
//#region src/composables/
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
1830
|
+
//#region src/composables/inputs/date-time-field.ts
|
|
1831
|
+
/**
|
|
1832
|
+
* The menu's content is a dialog with the pickers. `aria-owns` is removed since the input can't
|
|
1833
|
+
* own the content, and the menu still adds `aria-expanded` and `aria-controls`.
|
|
1834
|
+
*/
|
|
1835
|
+
const activatorProps = {
|
|
1836
|
+
role: "combobox",
|
|
1837
|
+
"aria-haspopup": "dialog",
|
|
1838
|
+
"aria-owns": void 0
|
|
616
1839
|
};
|
|
617
|
-
|
|
1840
|
+
/**
|
|
1841
|
+
* Extracts the date part (`YYYY-MM-DD`) of a value, or an empty string when
|
|
1842
|
+
* the type has no date.
|
|
1843
|
+
*/
|
|
1844
|
+
function getDatePart(value, type) {
|
|
1845
|
+
return type === "time" ? "" : value.split("T")[0];
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* Extracts the time part (`HH:mm`) of a value, or an empty string when the
|
|
1849
|
+
* value has no time.
|
|
1850
|
+
*/
|
|
1851
|
+
function getTimePart(value, type) {
|
|
1852
|
+
if (type === "time") return value;
|
|
1853
|
+
const [, time = ""] = value.split("T");
|
|
1854
|
+
return time;
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Builds a value for the given type, or an empty string while any required
|
|
1858
|
+
* part is missing.
|
|
1859
|
+
*/
|
|
1860
|
+
function joinDateTime(type, date, time) {
|
|
1861
|
+
if (type === "date") return date;
|
|
1862
|
+
if (type === "time") return time;
|
|
1863
|
+
return date && time ? `${date}T${time}` : "";
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Formats a value to be displayed in pt-BR, like `24/09/2026`, `14:30` or
|
|
1867
|
+
* `24/09/2026 14:30`.
|
|
1868
|
+
*/
|
|
1869
|
+
function formatDateTime$1(value, type) {
|
|
1870
|
+
return [getDatePart(value, type).split("-").reverse().join("/"), getTimePart(value, type)].filter(Boolean).join(" ");
|
|
1871
|
+
}
|
|
1872
|
+
function useDateTimeFieldRules(props) {
|
|
1873
|
+
const defaultRules = useRules(props);
|
|
618
1874
|
return computed(() => {
|
|
619
|
-
const
|
|
620
|
-
|
|
1875
|
+
const rules = [...defaultRules.value];
|
|
1876
|
+
if (props.min) rules.push(minRule(props.min, props.type));
|
|
1877
|
+
if (props.max) rules.push(maxRule(props.max, props.type));
|
|
1878
|
+
return rules;
|
|
621
1879
|
});
|
|
622
1880
|
}
|
|
623
|
-
|
|
1881
|
+
/**
|
|
1882
|
+
* Computes the limits of each picker. For `datetime`, time limits only apply
|
|
1883
|
+
* when the selected date is the limit's own date.
|
|
1884
|
+
*/
|
|
1885
|
+
function useDateTimeFieldPickerBounds(props, selectedDate) {
|
|
624
1886
|
return computed(() => {
|
|
625
|
-
|
|
1887
|
+
const min = props.min ?? "";
|
|
1888
|
+
const max = props.max ?? "";
|
|
1889
|
+
const date = toValue(selectedDate);
|
|
1890
|
+
return {
|
|
1891
|
+
dateMin: getDatePart(min, props.type),
|
|
1892
|
+
dateMax: getDatePart(max, props.type),
|
|
1893
|
+
timeMin: getTimeBound(min, props.type, date),
|
|
1894
|
+
timeMax: getTimeBound(max, props.type, date)
|
|
1895
|
+
};
|
|
626
1896
|
});
|
|
627
1897
|
}
|
|
628
|
-
|
|
629
|
-
|
|
1898
|
+
/**
|
|
1899
|
+
* Makes the field's input the activator of the pickers' menu, so the menu's attributes are on an
|
|
1900
|
+
* element whose role accepts them.
|
|
1901
|
+
* @param isOpen - Whether the menu is open.
|
|
1902
|
+
* @param isDisabled - Whether the menu can't open, as when the field is disabled or readonly.
|
|
1903
|
+
* @param hasLabel - Whether the field has a label, which names the menu's dialog.
|
|
1904
|
+
*/
|
|
1905
|
+
function useDateTimeFieldMenu(isOpen, isDisabled, hasLabel) {
|
|
1906
|
+
const inputId = useId();
|
|
1907
|
+
const activator = computed(() => toValue(isDisabled) ? void 0 : `#${inputId}`);
|
|
1908
|
+
const contentProps = computed(() => ({
|
|
1909
|
+
role: "dialog",
|
|
1910
|
+
"aria-labelledby": toValue(hasLabel) ? `${inputId}-label` : void 0
|
|
1911
|
+
}));
|
|
1912
|
+
function open() {
|
|
1913
|
+
if (!toValue(isDisabled)) isOpen.value = true;
|
|
1914
|
+
}
|
|
1915
|
+
return {
|
|
1916
|
+
inputId,
|
|
1917
|
+
activator,
|
|
1918
|
+
activatorProps,
|
|
1919
|
+
contentProps,
|
|
1920
|
+
open
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
function getTimeBound(bound, type, selectedDate) {
|
|
1924
|
+
if (type === "datetime" && getDatePart(bound, type) !== selectedDate) return "";
|
|
1925
|
+
return getTimePart(bound, type);
|
|
1926
|
+
}
|
|
1927
|
+
function minRule(min, type) {
|
|
1928
|
+
const message = `Valor anterior ao mínimo permitido (${formatDateTime$1(min, type)})`;
|
|
1929
|
+
return (value) => !value || value >= min || message;
|
|
1930
|
+
}
|
|
1931
|
+
function maxRule(max, type) {
|
|
1932
|
+
const message = `Valor posterior ao máximo permitido (${formatDateTime$1(max, type)})`;
|
|
1933
|
+
return (value) => !value || value <= max || message;
|
|
630
1934
|
}
|
|
631
1935
|
//#endregion
|
|
632
|
-
//#region src/components/
|
|
633
|
-
var
|
|
1936
|
+
//#region src/components/inputs/date-time-field/date-time-field.vue
|
|
1937
|
+
var date_time_field_default = /* @__PURE__ */ defineComponent({
|
|
634
1938
|
inheritAttrs: false,
|
|
635
|
-
__name: "
|
|
636
|
-
props: {
|
|
1939
|
+
__name: "date-time-field",
|
|
1940
|
+
props: /*@__PURE__*/ mergeModels({
|
|
637
1941
|
variant: {
|
|
638
1942
|
type: String,
|
|
639
|
-
required: false
|
|
1943
|
+
required: false,
|
|
1944
|
+
default: "primary"
|
|
640
1945
|
},
|
|
641
1946
|
type: {
|
|
1947
|
+
type: String,
|
|
1948
|
+
required: false,
|
|
1949
|
+
default: "datetime"
|
|
1950
|
+
},
|
|
1951
|
+
min: {
|
|
642
1952
|
type: String,
|
|
643
1953
|
required: false
|
|
644
1954
|
},
|
|
645
|
-
|
|
1955
|
+
max: {
|
|
646
1956
|
type: String,
|
|
647
1957
|
required: false
|
|
648
1958
|
},
|
|
@@ -654,7 +1964,27 @@ var button_default = /* @__PURE__ */ defineComponent({
|
|
|
654
1964
|
type: Boolean,
|
|
655
1965
|
required: false
|
|
656
1966
|
},
|
|
657
|
-
|
|
1967
|
+
clearable: {
|
|
1968
|
+
type: Boolean,
|
|
1969
|
+
required: false
|
|
1970
|
+
},
|
|
1971
|
+
readonly: {
|
|
1972
|
+
type: Boolean,
|
|
1973
|
+
required: false
|
|
1974
|
+
},
|
|
1975
|
+
required: {
|
|
1976
|
+
type: Boolean,
|
|
1977
|
+
required: false
|
|
1978
|
+
},
|
|
1979
|
+
label: {
|
|
1980
|
+
type: String,
|
|
1981
|
+
required: false
|
|
1982
|
+
},
|
|
1983
|
+
placeholder: {
|
|
1984
|
+
type: String,
|
|
1985
|
+
required: false
|
|
1986
|
+
},
|
|
1987
|
+
hint: {
|
|
658
1988
|
type: String,
|
|
659
1989
|
required: false
|
|
660
1990
|
},
|
|
@@ -662,72 +1992,319 @@ var button_default = /* @__PURE__ */ defineComponent({
|
|
|
662
1992
|
type: String,
|
|
663
1993
|
required: false
|
|
664
1994
|
}
|
|
665
|
-
},
|
|
666
|
-
|
|
667
|
-
|
|
1995
|
+
}, {
|
|
1996
|
+
"modelValue": {
|
|
1997
|
+
type: String,
|
|
1998
|
+
default: ""
|
|
1999
|
+
},
|
|
2000
|
+
"modelModifiers": {}
|
|
2001
|
+
}),
|
|
2002
|
+
emits: ["update:modelValue"],
|
|
2003
|
+
setup(__props) {
|
|
2004
|
+
const modelValue = useModel(__props, "modelValue");
|
|
668
2005
|
const props = __props;
|
|
669
|
-
const
|
|
670
|
-
const
|
|
671
|
-
const
|
|
672
|
-
const
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
2006
|
+
const messages = { "pt-BR": pt };
|
|
2007
|
+
const vuetifyVariant = useTextFieldVariant(() => props.variant);
|
|
2008
|
+
const rules = useDateTimeFieldRules(props);
|
|
2009
|
+
const displayValue = computed(() => formatDateTime$1(modelValue.value, props.type));
|
|
2010
|
+
const isClearable = computed(() => props.clearable && !props.readonly);
|
|
2011
|
+
const isMenuDisabled = computed(() => props.disabled || props.readonly);
|
|
2012
|
+
const isMenuOpen = ref(false);
|
|
2013
|
+
const draft = ref({});
|
|
2014
|
+
const draftDate = computed(() => draft.value.date ?? getDatePart(modelValue.value, props.type));
|
|
2015
|
+
const draftTime = computed(() => draft.value.time ?? getTimePart(modelValue.value, props.type));
|
|
2016
|
+
const draftValue = computed(() => joinDateTime(props.type, draftDate.value, draftTime.value));
|
|
2017
|
+
const bounds = useDateTimeFieldPickerBounds(props, draftDate);
|
|
2018
|
+
const { inputId, activator, activatorProps, contentProps, open: openMenu } = useDateTimeFieldMenu(isMenuOpen, isMenuDisabled, () => !!props.label);
|
|
2019
|
+
watch(isMenuOpen, () => {
|
|
2020
|
+
draft.value = {};
|
|
2021
|
+
});
|
|
2022
|
+
function onDateUpdate(date) {
|
|
2023
|
+
draft.value.date = formatDateTime(date, "YYYY-MM-DD");
|
|
2024
|
+
commitDraft();
|
|
2025
|
+
closeIfComplete();
|
|
2026
|
+
}
|
|
2027
|
+
function onTimeUpdate(time) {
|
|
2028
|
+
draft.value.time = time ?? "";
|
|
2029
|
+
commitDraft();
|
|
2030
|
+
}
|
|
2031
|
+
function commitDraft() {
|
|
2032
|
+
if (draftValue.value) modelValue.value = draftValue.value;
|
|
2033
|
+
}
|
|
2034
|
+
function closeIfComplete() {
|
|
2035
|
+
if (draftValue.value) isMenuOpen.value = false;
|
|
2036
|
+
}
|
|
2037
|
+
function clear() {
|
|
2038
|
+
modelValue.value = "";
|
|
676
2039
|
}
|
|
677
2040
|
return (_ctx, _cache) => {
|
|
678
|
-
return openBlock(), createBlock(unref(
|
|
2041
|
+
return openBlock(), createBlock(unref(VTextField), {
|
|
2042
|
+
id: unref(inputId),
|
|
2043
|
+
"model-value": displayValue.value,
|
|
2044
|
+
"validation-value": modelValue.value,
|
|
2045
|
+
variant: unref(vuetifyVariant),
|
|
2046
|
+
rules: unref(rules),
|
|
2047
|
+
"data-testid": props.dataTestid,
|
|
679
2048
|
disabled: props.disabled,
|
|
680
2049
|
loading: props.loading,
|
|
681
|
-
"
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
2050
|
+
"aria-busy": props.loading || void 0,
|
|
2051
|
+
label: props.label,
|
|
2052
|
+
placeholder: props.placeholder,
|
|
2053
|
+
hint: props.hint,
|
|
2054
|
+
clearable: isClearable.value,
|
|
2055
|
+
readonly: "",
|
|
2056
|
+
"onClick:clear": clear,
|
|
2057
|
+
"onClick:control": unref(openMenu)
|
|
687
2058
|
}, {
|
|
688
|
-
|
|
689
|
-
|
|
2059
|
+
loader: withCtx((loader) => [createVNode(field_loader_default, normalizeProps(guardReactiveProps(loader)), null, 16)]),
|
|
2060
|
+
default: withCtx(() => [createVNode(unref(VMenu), {
|
|
2061
|
+
modelValue: isMenuOpen.value,
|
|
2062
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isMenuOpen.value = $event),
|
|
2063
|
+
activator: unref(activator),
|
|
2064
|
+
target: "parent",
|
|
2065
|
+
"activator-props": unref(activatorProps),
|
|
2066
|
+
"content-props": unref(contentProps),
|
|
2067
|
+
location: "bottom start",
|
|
2068
|
+
"min-width": "0",
|
|
2069
|
+
disabled: isMenuDisabled.value,
|
|
2070
|
+
"close-on-content-click": false
|
|
2071
|
+
}, {
|
|
2072
|
+
default: withCtx(() => [createVNode(unref(VCard), null, {
|
|
2073
|
+
default: withCtx(() => [createVNode(unref(VLocaleProvider), {
|
|
2074
|
+
locale: "pt-BR",
|
|
2075
|
+
messages,
|
|
2076
|
+
class: "d-flex flex-wrap"
|
|
2077
|
+
}, {
|
|
2078
|
+
default: withCtx(() => [props.type !== "time" ? (openBlock(), createBlock(unref(VDatePicker), {
|
|
2079
|
+
key: 0,
|
|
2080
|
+
"model-value": draftDate.value || null,
|
|
2081
|
+
min: unref(bounds).dateMin,
|
|
2082
|
+
max: unref(bounds).dateMax,
|
|
2083
|
+
"onUpdate:modelValue": onDateUpdate
|
|
2084
|
+
}, null, 8, [
|
|
2085
|
+
"model-value",
|
|
2086
|
+
"min",
|
|
2087
|
+
"max"
|
|
2088
|
+
])) : createCommentVNode("v-if", true), props.type !== "date" ? (openBlock(), createBlock(unref(VTimePicker), {
|
|
2089
|
+
key: 1,
|
|
2090
|
+
"model-value": draftTime.value,
|
|
2091
|
+
min: unref(bounds).timeMin,
|
|
2092
|
+
max: unref(bounds).timeMax,
|
|
2093
|
+
format: "24hr",
|
|
2094
|
+
"onUpdate:modelValue": onTimeUpdate,
|
|
2095
|
+
"onUpdate:minute": closeIfComplete
|
|
2096
|
+
}, null, 8, [
|
|
2097
|
+
"model-value",
|
|
2098
|
+
"min",
|
|
2099
|
+
"max"
|
|
2100
|
+
])) : createCommentVNode("v-if", true)]),
|
|
2101
|
+
_: 1
|
|
2102
|
+
})]),
|
|
2103
|
+
_: 1
|
|
2104
|
+
})]),
|
|
2105
|
+
_: 1
|
|
2106
|
+
}, 8, [
|
|
2107
|
+
"modelValue",
|
|
2108
|
+
"activator",
|
|
2109
|
+
"activator-props",
|
|
2110
|
+
"content-props",
|
|
2111
|
+
"disabled"
|
|
2112
|
+
])]),
|
|
2113
|
+
_: 1
|
|
690
2114
|
}, 8, [
|
|
2115
|
+
"id",
|
|
2116
|
+
"model-value",
|
|
2117
|
+
"validation-value",
|
|
2118
|
+
"variant",
|
|
2119
|
+
"rules",
|
|
2120
|
+
"data-testid",
|
|
691
2121
|
"disabled",
|
|
692
2122
|
"loading",
|
|
693
|
-
"
|
|
694
|
-
"
|
|
695
|
-
"
|
|
696
|
-
"
|
|
697
|
-
"
|
|
2123
|
+
"aria-busy",
|
|
2124
|
+
"label",
|
|
2125
|
+
"placeholder",
|
|
2126
|
+
"hint",
|
|
2127
|
+
"clearable",
|
|
2128
|
+
"onClick:control"
|
|
698
2129
|
]);
|
|
699
2130
|
};
|
|
700
2131
|
}
|
|
701
2132
|
});
|
|
702
2133
|
//#endregion
|
|
703
|
-
//#region src/
|
|
704
|
-
|
|
2134
|
+
//#region src/composables/inputs/mask-field.ts
|
|
2135
|
+
/**
|
|
2136
|
+
* Masks of the presets, as Vuetify reads them: `#` is a digit and `N` is a
|
|
2137
|
+
* letter or a digit, with letters converted to uppercase. Our preset names
|
|
2138
|
+
* always map to these masks, never to Vuetify's own presets (e.g. its American
|
|
2139
|
+
* `phone`).
|
|
2140
|
+
*/
|
|
2141
|
+
const maskFieldPresets = {
|
|
2142
|
+
cpf: "###.###.###-##",
|
|
2143
|
+
cnpj: "NN.NNN.NNN/NNNN-##",
|
|
2144
|
+
cep: "#####-###",
|
|
2145
|
+
phone: "(##) #####-####",
|
|
2146
|
+
date: "##/##/####"
|
|
2147
|
+
};
|
|
2148
|
+
/**
|
|
2149
|
+
* Returns the mask as Vuetify reads it: the mask of a preset, or the given mask
|
|
2150
|
+
* itself, escaped, when it's a custom one.
|
|
2151
|
+
*
|
|
2152
|
+
* It takes any string, as the `mask` prop may get one through a cast or from
|
|
2153
|
+
* JavaScript: a string that isn't a preset is taken as the mask.
|
|
2154
|
+
*/
|
|
2155
|
+
function resolveMask(mask) {
|
|
2156
|
+
return isMaskFieldPreset(mask) ? maskFieldPresets[mask] : toVuetifyMask(mask);
|
|
2157
|
+
}
|
|
2158
|
+
/**
|
|
2159
|
+
* Returns a custom mask as Vuetify reads it. `#` is the only token of custom
|
|
2160
|
+
* masks, but Vuetify always adds its own tokens (`A`, `a`, `N`, `n` and `X`)
|
|
2161
|
+
* and its escape (`\`), so these characters are escaped to stay literals.
|
|
2162
|
+
*/
|
|
2163
|
+
function toVuetifyMask(mask) {
|
|
2164
|
+
return mask.replaceAll(/[AaNnX\\]/g, String.raw`\$&`);
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Returns the tokens of a mask as Vuetify reads it (see {@link resolveMask}),
|
|
2168
|
+
* in order, without its literals (e.g. `NNNNNNNNNNNN##` for `cnpj`).
|
|
2169
|
+
*/
|
|
2170
|
+
function getMaskTokens(mask) {
|
|
2171
|
+
return mask.replaceAll(/\\.|[^#N]/g, "");
|
|
2172
|
+
}
|
|
2173
|
+
/**
|
|
2174
|
+
* Returns the default placeholder of a mask as Vuetify reads it (see
|
|
2175
|
+
* {@link resolveMask}): the mask with `0` in place of each token, keeping its
|
|
2176
|
+
* literals (e.g. `000.000.000-00` for `cpf` and `Nº 0000` for `Nº ####`).
|
|
2177
|
+
*/
|
|
2178
|
+
function getMaskPlaceholder(mask) {
|
|
2179
|
+
return mask.replaceAll(/\\(.)|[#N]/g, (_token, literal) => literal ?? "0");
|
|
2180
|
+
}
|
|
2181
|
+
function isMaskFieldPreset(mask) {
|
|
2182
|
+
return Object.hasOwn(maskFieldPresets, mask);
|
|
2183
|
+
}
|
|
2184
|
+
/**
|
|
2185
|
+
* Keeps the input masked while the user edits it, emitting only the
|
|
2186
|
+
* characters of the tokens (Vuetify's `unmask`), with letters in uppercase.
|
|
2187
|
+
*
|
|
2188
|
+
* A `v-model` out of the mask's format (masked, with lowercase letters, or with
|
|
2189
|
+
* characters or length the mask doesn't accept) is displayed normalized,
|
|
2190
|
+
* without being emitted, and validated as it is, so a value that doesn't fit
|
|
2191
|
+
* the mask fails validation.
|
|
2192
|
+
*/
|
|
2193
|
+
function useMaskField(props, model) {
|
|
2194
|
+
const mask = computed(() => resolveMask(props.mask));
|
|
2195
|
+
const tokens = computed(() => getMaskTokens(mask.value));
|
|
2196
|
+
const acceptsLetters = computed(() => tokens.value.includes("N"));
|
|
2197
|
+
const vuetifyMask = useMask({ get mask() {
|
|
2198
|
+
return {
|
|
2199
|
+
mask: mask.value,
|
|
2200
|
+
tokens: {}
|
|
2201
|
+
};
|
|
2202
|
+
} });
|
|
2203
|
+
const displayValue = computed(() => vuetifyMask.mask(unmask(model.value)));
|
|
2204
|
+
const placeholder = computed(() => props.placeholder ?? getMaskPlaceholder(mask.value));
|
|
2205
|
+
const inputMode = computed(() => acceptsLetters.value ? "text" : "numeric");
|
|
2206
|
+
const autocapitalize = computed(() => acceptsLetters.value ? "characters" : void 0);
|
|
2207
|
+
const defaultRules = useRules(props);
|
|
2208
|
+
const rules = computed(() => [
|
|
2209
|
+
...defaultRules.value,
|
|
2210
|
+
(value) => !value || !isValid(value) || isComplete(value) || "Valor incompleto",
|
|
2211
|
+
(value) => !value || isValid(value) || "Valor inválido"
|
|
2212
|
+
]);
|
|
2213
|
+
function unmask(text) {
|
|
2214
|
+
return vuetifyMask.unmask(text) ?? "";
|
|
2215
|
+
}
|
|
2216
|
+
function isValid(value) {
|
|
2217
|
+
return unmask(value).toUpperCase() === unmask(vuetifyMask.mask(value));
|
|
2218
|
+
}
|
|
2219
|
+
function isComplete(value) {
|
|
2220
|
+
return unmask(vuetifyMask.mask(value)).length === tokens.value.length;
|
|
2221
|
+
}
|
|
2222
|
+
/**
|
|
2223
|
+
* Returns the position in a masked text right after the given number of
|
|
2224
|
+
* characters of the value, skipping the literals after it unless the user
|
|
2225
|
+
* deleted backwards (as Vuetify's mask input does).
|
|
2226
|
+
*/
|
|
2227
|
+
function findCaret(masked, valueLength, deletedBackwards) {
|
|
2228
|
+
let caret = 0;
|
|
2229
|
+
let found = 0;
|
|
2230
|
+
while (found < valueLength) {
|
|
2231
|
+
if (!vuetifyMask.isDelimiter(masked, caret)) found++;
|
|
2232
|
+
caret++;
|
|
2233
|
+
}
|
|
2234
|
+
if (!deletedBackwards) while (vuetifyMask.isDelimiter(masked, caret)) caret++;
|
|
2235
|
+
return caret;
|
|
2236
|
+
}
|
|
2237
|
+
function update(input, deletedBackwards) {
|
|
2238
|
+
const caret = input.selectionStart ?? input.value.length;
|
|
2239
|
+
const masked = vuetifyMask.mask(unmask(input.value));
|
|
2240
|
+
const newCaret = findCaret(masked, unmask(vuetifyMask.mask(unmask(input.value.slice(0, caret)))).length, deletedBackwards);
|
|
2241
|
+
const value = unmask(masked);
|
|
2242
|
+
input.value = masked;
|
|
2243
|
+
input.setSelectionRange(newCaret, newCaret);
|
|
2244
|
+
if (value !== unmask(displayValue.value)) model.value = value;
|
|
2245
|
+
}
|
|
2246
|
+
function onInput(event) {
|
|
2247
|
+
if (!event.isComposing) update(event.target, event.inputType.endsWith("Backward"));
|
|
2248
|
+
}
|
|
2249
|
+
function onCompositionEnd(event) {
|
|
2250
|
+
update(event.target, false);
|
|
2251
|
+
}
|
|
2252
|
+
return {
|
|
2253
|
+
displayValue,
|
|
2254
|
+
placeholder,
|
|
2255
|
+
inputMode,
|
|
2256
|
+
autocapitalize,
|
|
2257
|
+
rules,
|
|
2258
|
+
onInput,
|
|
2259
|
+
onCompositionEnd
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
//#endregion
|
|
2263
|
+
//#region src/components/inputs/mask-field/mask-field.vue
|
|
2264
|
+
var mask_field_default = /* @__PURE__ */ defineComponent({
|
|
705
2265
|
inheritAttrs: false,
|
|
706
|
-
__name: "
|
|
2266
|
+
__name: "mask-field",
|
|
707
2267
|
props: /*@__PURE__*/ mergeModels({
|
|
708
|
-
|
|
2268
|
+
variant: {
|
|
709
2269
|
type: String,
|
|
710
|
-
required: false
|
|
2270
|
+
required: false,
|
|
2271
|
+
default: "primary"
|
|
2272
|
+
},
|
|
2273
|
+
mask: {
|
|
2274
|
+
type: null,
|
|
2275
|
+
required: true
|
|
711
2276
|
},
|
|
712
2277
|
disabled: {
|
|
713
2278
|
type: Boolean,
|
|
714
2279
|
required: false
|
|
715
2280
|
},
|
|
716
|
-
|
|
2281
|
+
loading: {
|
|
717
2282
|
type: Boolean,
|
|
718
2283
|
required: false
|
|
719
2284
|
},
|
|
720
|
-
|
|
2285
|
+
clearable: {
|
|
721
2286
|
type: Boolean,
|
|
722
|
-
required: false
|
|
723
|
-
skipCheck: true,
|
|
724
|
-
default: true
|
|
2287
|
+
required: false
|
|
725
2288
|
},
|
|
726
|
-
|
|
2289
|
+
readonly: {
|
|
727
2290
|
type: Boolean,
|
|
728
|
-
required: false
|
|
729
|
-
|
|
730
|
-
|
|
2291
|
+
required: false
|
|
2292
|
+
},
|
|
2293
|
+
required: {
|
|
2294
|
+
type: Boolean,
|
|
2295
|
+
required: false
|
|
2296
|
+
},
|
|
2297
|
+
label: {
|
|
2298
|
+
type: String,
|
|
2299
|
+
required: false
|
|
2300
|
+
},
|
|
2301
|
+
placeholder: {
|
|
2302
|
+
type: String,
|
|
2303
|
+
required: false
|
|
2304
|
+
},
|
|
2305
|
+
hint: {
|
|
2306
|
+
type: String,
|
|
2307
|
+
required: false
|
|
731
2308
|
},
|
|
732
2309
|
dataTestid: {
|
|
733
2310
|
type: String,
|
|
@@ -735,55 +2312,161 @@ var checkbox_default = /* @__PURE__ */ defineComponent({
|
|
|
735
2312
|
}
|
|
736
2313
|
}, {
|
|
737
2314
|
"modelValue": {
|
|
738
|
-
type:
|
|
739
|
-
default:
|
|
2315
|
+
type: String,
|
|
2316
|
+
default: ""
|
|
740
2317
|
},
|
|
741
2318
|
"modelModifiers": {}
|
|
742
2319
|
}),
|
|
743
2320
|
emits: ["update:modelValue"],
|
|
744
2321
|
setup(__props) {
|
|
745
|
-
const props = __props;
|
|
746
2322
|
const modelValue = useModel(__props, "modelValue");
|
|
2323
|
+
const props = __props;
|
|
2324
|
+
const vuetifyVariant = useTextFieldVariant(() => props.variant);
|
|
2325
|
+
const { displayValue, placeholder, inputMode, autocapitalize, rules, onInput, onCompositionEnd } = useMaskField(props, modelValue);
|
|
2326
|
+
function keepFieldControlled() {}
|
|
2327
|
+
function clear() {
|
|
2328
|
+
modelValue.value = "";
|
|
2329
|
+
}
|
|
747
2330
|
return (_ctx, _cache) => {
|
|
748
|
-
return openBlock(), createBlock(unref(
|
|
749
|
-
|
|
750
|
-
"
|
|
751
|
-
|
|
752
|
-
|
|
2331
|
+
return openBlock(), createBlock(unref(VTextField), {
|
|
2332
|
+
"model-value": unref(displayValue),
|
|
2333
|
+
"validation-value": modelValue.value,
|
|
2334
|
+
variant: unref(vuetifyVariant),
|
|
2335
|
+
rules: unref(rules),
|
|
753
2336
|
"data-testid": props.dataTestid,
|
|
754
2337
|
disabled: props.disabled,
|
|
2338
|
+
loading: props.loading,
|
|
2339
|
+
"aria-busy": props.loading || void 0,
|
|
755
2340
|
readonly: props.readonly,
|
|
756
2341
|
label: props.label,
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
"
|
|
761
|
-
|
|
762
|
-
|
|
2342
|
+
placeholder: unref(placeholder),
|
|
2343
|
+
hint: props.hint,
|
|
2344
|
+
clearable: props.clearable,
|
|
2345
|
+
type: "text",
|
|
2346
|
+
inputmode: unref(inputMode),
|
|
2347
|
+
autocapitalize: unref(autocapitalize),
|
|
2348
|
+
"onUpdate:modelValue": keepFieldControlled,
|
|
2349
|
+
onInput: unref(onInput),
|
|
2350
|
+
onCompositionend: unref(onCompositionEnd),
|
|
2351
|
+
"onClick:clear": clear
|
|
2352
|
+
}, {
|
|
2353
|
+
loader: withCtx((loader) => [createVNode(field_loader_default, normalizeProps(guardReactiveProps(loader)), null, 16)]),
|
|
2354
|
+
_: 1
|
|
2355
|
+
}, 8, [
|
|
2356
|
+
"model-value",
|
|
2357
|
+
"validation-value",
|
|
2358
|
+
"variant",
|
|
2359
|
+
"rules",
|
|
763
2360
|
"data-testid",
|
|
764
2361
|
"disabled",
|
|
2362
|
+
"loading",
|
|
2363
|
+
"aria-busy",
|
|
765
2364
|
"readonly",
|
|
766
|
-
"label"
|
|
2365
|
+
"label",
|
|
2366
|
+
"placeholder",
|
|
2367
|
+
"hint",
|
|
2368
|
+
"clearable",
|
|
2369
|
+
"inputmode",
|
|
2370
|
+
"autocapitalize",
|
|
2371
|
+
"onInput",
|
|
2372
|
+
"onCompositionend"
|
|
767
2373
|
]);
|
|
768
2374
|
};
|
|
769
2375
|
}
|
|
770
2376
|
});
|
|
771
2377
|
//#endregion
|
|
2378
|
+
//#region src/composables/alignment/alignment.ts
|
|
2379
|
+
const alignXToClass = {
|
|
2380
|
+
start: "justify-start",
|
|
2381
|
+
center: "justify-center",
|
|
2382
|
+
end: "justify-end"
|
|
2383
|
+
};
|
|
2384
|
+
const alignYToClasses = {
|
|
2385
|
+
start: {
|
|
2386
|
+
items: "align-start",
|
|
2387
|
+
content: "align-content-start"
|
|
2388
|
+
},
|
|
2389
|
+
center: {
|
|
2390
|
+
items: "align-center",
|
|
2391
|
+
content: "align-content-center"
|
|
2392
|
+
},
|
|
2393
|
+
end: {
|
|
2394
|
+
items: "align-end",
|
|
2395
|
+
content: "align-content-end"
|
|
2396
|
+
},
|
|
2397
|
+
stretch: {
|
|
2398
|
+
items: "align-stretch",
|
|
2399
|
+
content: "align-content-stretch"
|
|
2400
|
+
}
|
|
2401
|
+
};
|
|
2402
|
+
/**
|
|
2403
|
+
* Vuetify's `justify-*` utility class for components with an `alignX` prop, laid out with flexbox.
|
|
2404
|
+
* Falls back to `"start"`.
|
|
2405
|
+
*/
|
|
2406
|
+
function useAlignX(alignX) {
|
|
2407
|
+
return computed(() => alignXToClass[toValue(alignX) ?? "start"]);
|
|
2408
|
+
}
|
|
2409
|
+
/**
|
|
2410
|
+
* Vuetify's `align-*` and `align-content-*` utility classes for components with an `alignY` prop,
|
|
2411
|
+
* laid out with flexbox. Falls back to `"start"`.
|
|
2412
|
+
*/
|
|
2413
|
+
function useAlignY(alignY) {
|
|
2414
|
+
return computed(() => alignYToClasses[toValue(alignY) ?? "start"]);
|
|
2415
|
+
}
|
|
2416
|
+
//#endregion
|
|
772
2417
|
//#region src/components/grid/row/row.vue
|
|
773
2418
|
var row_default = /* @__PURE__ */ defineComponent({
|
|
774
2419
|
inheritAttrs: false,
|
|
775
2420
|
__name: "row",
|
|
776
|
-
props: {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
2421
|
+
props: {
|
|
2422
|
+
alignX: {
|
|
2423
|
+
type: String,
|
|
2424
|
+
required: false
|
|
2425
|
+
},
|
|
2426
|
+
alignY: {
|
|
2427
|
+
type: String,
|
|
2428
|
+
required: false
|
|
2429
|
+
},
|
|
2430
|
+
fullHeight: {
|
|
2431
|
+
type: Boolean,
|
|
2432
|
+
required: false
|
|
2433
|
+
},
|
|
2434
|
+
list: {
|
|
2435
|
+
type: Boolean,
|
|
2436
|
+
required: false
|
|
2437
|
+
},
|
|
2438
|
+
dataTestid: {
|
|
2439
|
+
type: String,
|
|
2440
|
+
required: false
|
|
2441
|
+
}
|
|
2442
|
+
},
|
|
780
2443
|
setup(__props) {
|
|
781
2444
|
const props = __props;
|
|
2445
|
+
const alignXClass = useAlignX(() => props.alignX);
|
|
2446
|
+
const alignYClasses = useAlignY(() => props.alignY);
|
|
2447
|
+
const heightClass = useFullHeight(() => props.fullHeight);
|
|
2448
|
+
const alignContentClass = computed(() => props.fullHeight ? alignYClasses.value.content : void 0);
|
|
2449
|
+
const tag = computed(() => props.list ? "ul" : void 0);
|
|
2450
|
+
const listClasses = computed(() => props.list ? "pa-0 ma-0" : void 0);
|
|
782
2451
|
return (_ctx, _cache) => {
|
|
783
|
-
return openBlock(), createBlock(unref(VRow), {
|
|
2452
|
+
return openBlock(), createBlock(unref(VRow), {
|
|
2453
|
+
tag: tag.value,
|
|
2454
|
+
class: normalizeClass([
|
|
2455
|
+
unref(alignXClass),
|
|
2456
|
+
unref(alignYClasses).items,
|
|
2457
|
+
unref(heightClass),
|
|
2458
|
+
alignContentClass.value,
|
|
2459
|
+
listClasses.value
|
|
2460
|
+
]),
|
|
2461
|
+
"data-testid": props.dataTestid
|
|
2462
|
+
}, {
|
|
784
2463
|
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
785
2464
|
_: 3
|
|
786
|
-
}, 8, [
|
|
2465
|
+
}, 8, [
|
|
2466
|
+
"tag",
|
|
2467
|
+
"class",
|
|
2468
|
+
"data-testid"
|
|
2469
|
+
]);
|
|
787
2470
|
};
|
|
788
2471
|
}
|
|
789
2472
|
});
|
|
@@ -798,6 +2481,10 @@ var column_default = /* @__PURE__ */ defineComponent({
|
|
|
798
2481
|
required: false,
|
|
799
2482
|
default: "12"
|
|
800
2483
|
},
|
|
2484
|
+
listItem: {
|
|
2485
|
+
type: Boolean,
|
|
2486
|
+
required: false
|
|
2487
|
+
},
|
|
801
2488
|
dataTestid: {
|
|
802
2489
|
type: String,
|
|
803
2490
|
required: false
|
|
@@ -805,15 +2492,24 @@ var column_default = /* @__PURE__ */ defineComponent({
|
|
|
805
2492
|
},
|
|
806
2493
|
setup(__props) {
|
|
807
2494
|
const props = __props;
|
|
2495
|
+
const tag = computed(() => props.listItem ? "li" : void 0);
|
|
2496
|
+
const listItemClasses = computed(() => props.listItem ? "d-block" : void 0);
|
|
808
2497
|
return (_ctx, _cache) => {
|
|
809
2498
|
return openBlock(), createBlock(unref(VCol), {
|
|
810
2499
|
cols: "12",
|
|
811
2500
|
sm: props.cols,
|
|
2501
|
+
tag: tag.value,
|
|
2502
|
+
class: normalizeClass(listItemClasses.value),
|
|
812
2503
|
"data-testid": props.dataTestid
|
|
813
2504
|
}, {
|
|
814
2505
|
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
815
2506
|
_: 3
|
|
816
|
-
}, 8, [
|
|
2507
|
+
}, 8, [
|
|
2508
|
+
"sm",
|
|
2509
|
+
"tag",
|
|
2510
|
+
"class",
|
|
2511
|
+
"data-testid"
|
|
2512
|
+
]);
|
|
817
2513
|
};
|
|
818
2514
|
}
|
|
819
2515
|
});
|
|
@@ -878,6 +2574,26 @@ function useSectionVariant(variant) {
|
|
|
878
2574
|
return variantValue ? sectionVariantToVuetifyVariant[variantValue] : sectionVariantToVuetifyVariant.primary;
|
|
879
2575
|
});
|
|
880
2576
|
}
|
|
2577
|
+
const sectionTextAlignToClasses = {
|
|
2578
|
+
start: {
|
|
2579
|
+
content: "text-start",
|
|
2580
|
+
actions: "justify-end"
|
|
2581
|
+
},
|
|
2582
|
+
center: {
|
|
2583
|
+
content: "text-center",
|
|
2584
|
+
actions: "justify-center"
|
|
2585
|
+
},
|
|
2586
|
+
end: {
|
|
2587
|
+
content: "text-end",
|
|
2588
|
+
actions: "justify-end"
|
|
2589
|
+
}
|
|
2590
|
+
};
|
|
2591
|
+
function useSectionTextAlign(textAlign) {
|
|
2592
|
+
return computed(() => {
|
|
2593
|
+
const textAlignValue = toValue(textAlign);
|
|
2594
|
+
return textAlignValue ? sectionTextAlignToClasses[textAlignValue] : sectionTextAlignToClasses.start;
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
881
2597
|
//#endregion
|
|
882
2598
|
//#region src/components/sections/section/section.vue
|
|
883
2599
|
var section_default = /* @__PURE__ */ defineComponent({
|
|
@@ -896,6 +2612,10 @@ var section_default = /* @__PURE__ */ defineComponent({
|
|
|
896
2612
|
type: String,
|
|
897
2613
|
required: false
|
|
898
2614
|
},
|
|
2615
|
+
textAlign: {
|
|
2616
|
+
type: String,
|
|
2617
|
+
required: false
|
|
2618
|
+
},
|
|
899
2619
|
actions: {
|
|
900
2620
|
type: Array,
|
|
901
2621
|
required: false
|
|
@@ -920,31 +2640,35 @@ var section_default = /* @__PURE__ */ defineComponent({
|
|
|
920
2640
|
setup(__props) {
|
|
921
2641
|
const props = __props;
|
|
922
2642
|
const variant = useSectionVariant(() => props.variant);
|
|
2643
|
+
const textAlignClasses = useSectionTextAlign(() => props.textAlign);
|
|
923
2644
|
const title = computed(() => props.title || void 0);
|
|
924
2645
|
const subtitle = computed(() => props.subtitle || void 0);
|
|
925
|
-
const
|
|
926
|
-
const
|
|
2646
|
+
const widthClass = useFullWidth(() => props.fullWidth);
|
|
2647
|
+
const heightClass = useFullHeight(() => props.fullHeight);
|
|
927
2648
|
const showActions = computed(() => !!props.actions?.length);
|
|
928
2649
|
const skeletonType = computed(() => showActions.value ? "image, actions" : "image");
|
|
929
2650
|
return (_ctx, _cache) => {
|
|
930
|
-
return openBlock(), createBlock(
|
|
2651
|
+
return openBlock(), createBlock(skeleton_loader_default, {
|
|
931
2652
|
loading: props.loading,
|
|
932
2653
|
type: skeletonType.value,
|
|
933
|
-
width:
|
|
934
|
-
height:
|
|
2654
|
+
"full-width": props.fullWidth,
|
|
2655
|
+
"full-height": props.fullHeight
|
|
935
2656
|
}, {
|
|
936
|
-
default: withCtx(() => [createVNode(unref(VCard), {
|
|
2657
|
+
default: withCtx(() => [createCommentVNode(" The card and its actions have no alignment props, so `textAlign` uses utility classes "), createVNode(unref(VCard), {
|
|
937
2658
|
title: title.value,
|
|
938
2659
|
subtitle: subtitle.value,
|
|
939
2660
|
variant: unref(variant),
|
|
940
|
-
width: width.value,
|
|
941
|
-
height: height.value,
|
|
942
2661
|
"data-testid": props.dataTestid,
|
|
943
|
-
class:
|
|
2662
|
+
class: normalizeClass([
|
|
2663
|
+
"d-flex flex-column",
|
|
2664
|
+
unref(widthClass),
|
|
2665
|
+
unref(heightClass),
|
|
2666
|
+
unref(textAlignClasses).content
|
|
2667
|
+
])
|
|
944
2668
|
}, {
|
|
945
2669
|
default: withCtx(() => [renderSlot(_ctx.$slots, "default"), showActions.value ? (openBlock(), createBlock(unref(VCardActions), {
|
|
946
2670
|
key: 0,
|
|
947
|
-
class: "mt-auto"
|
|
2671
|
+
class: normalizeClass(["mt-auto", unref(textAlignClasses).actions])
|
|
948
2672
|
}, {
|
|
949
2673
|
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.actions, ({ label, onClick, ...action }, index) => {
|
|
950
2674
|
return openBlock(), createBlock(button_default, mergeProps({ key: index }, { ref_for: true }, action, { onClick }), {
|
|
@@ -953,22 +2677,21 @@ var section_default = /* @__PURE__ */ defineComponent({
|
|
|
953
2677
|
}, 1040, ["onClick"]);
|
|
954
2678
|
}), 128))]),
|
|
955
2679
|
_: 1
|
|
956
|
-
})) : createCommentVNode("v-if", true)]),
|
|
2680
|
+
}, 8, ["class"])) : createCommentVNode("v-if", true)]),
|
|
957
2681
|
_: 3
|
|
958
2682
|
}, 8, [
|
|
959
2683
|
"title",
|
|
960
2684
|
"subtitle",
|
|
961
2685
|
"variant",
|
|
962
|
-
"
|
|
963
|
-
"
|
|
964
|
-
"data-testid"
|
|
2686
|
+
"data-testid",
|
|
2687
|
+
"class"
|
|
965
2688
|
])]),
|
|
966
2689
|
_: 3
|
|
967
2690
|
}, 8, [
|
|
968
2691
|
"loading",
|
|
969
2692
|
"type",
|
|
970
|
-
"width",
|
|
971
|
-
"height"
|
|
2693
|
+
"full-width",
|
|
2694
|
+
"full-height"
|
|
972
2695
|
]);
|
|
973
2696
|
};
|
|
974
2697
|
}
|
|
@@ -1018,7 +2741,7 @@ var table_default = /* @__PURE__ */ defineComponent({
|
|
|
1018
2741
|
return `${props.dataTestid}-${index}`;
|
|
1019
2742
|
}
|
|
1020
2743
|
return (_ctx, _cache) => {
|
|
1021
|
-
return openBlock(), createBlock(
|
|
2744
|
+
return openBlock(), createBlock(skeleton_loader_default, {
|
|
1022
2745
|
loading: props.loading,
|
|
1023
2746
|
type: type.value
|
|
1024
2747
|
}, {
|
|
@@ -1056,4 +2779,448 @@ var table_default = /* @__PURE__ */ defineComponent({
|
|
|
1056
2779
|
}
|
|
1057
2780
|
});
|
|
1058
2781
|
//#endregion
|
|
1059
|
-
|
|
2782
|
+
//#region src/components/details/details.vue
|
|
2783
|
+
var details_default = /* @__PURE__ */ defineComponent({
|
|
2784
|
+
inheritAttrs: false,
|
|
2785
|
+
__name: "details",
|
|
2786
|
+
props: {
|
|
2787
|
+
variant: {
|
|
2788
|
+
type: String,
|
|
2789
|
+
required: false
|
|
2790
|
+
},
|
|
2791
|
+
title: {
|
|
2792
|
+
type: String,
|
|
2793
|
+
required: false
|
|
2794
|
+
},
|
|
2795
|
+
loading: {
|
|
2796
|
+
type: Boolean,
|
|
2797
|
+
required: false
|
|
2798
|
+
},
|
|
2799
|
+
dataTestid: {
|
|
2800
|
+
type: String,
|
|
2801
|
+
required: false
|
|
2802
|
+
}
|
|
2803
|
+
},
|
|
2804
|
+
setup(__props) {
|
|
2805
|
+
const props = __props;
|
|
2806
|
+
const outlined = computed(() => props.variant === "secondary");
|
|
2807
|
+
return (_ctx, _cache) => {
|
|
2808
|
+
return openBlock(), createBlock(skeleton_loader_default, {
|
|
2809
|
+
loading: props.loading,
|
|
2810
|
+
type: "heading",
|
|
2811
|
+
"full-width": ""
|
|
2812
|
+
}, {
|
|
2813
|
+
default: withCtx(() => [createVNode(unref(VExpansionPanels), {
|
|
2814
|
+
flat: outlined.value,
|
|
2815
|
+
"data-testid": props.dataTestid
|
|
2816
|
+
}, {
|
|
2817
|
+
default: withCtx(() => [createVNode(unref(VExpansionPanel), { class: normalizeClass({ border: outlined.value }) }, {
|
|
2818
|
+
default: withCtx(() => [createVNode(unref(VExpansionPanelTitle), null, {
|
|
2819
|
+
default: withCtx(() => [createTextVNode(toDisplayString(props.title), 1)]),
|
|
2820
|
+
_: 1
|
|
2821
|
+
}), createVNode(unref(VExpansionPanelText), null, {
|
|
2822
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
2823
|
+
_: 3
|
|
2824
|
+
})]),
|
|
2825
|
+
_: 3
|
|
2826
|
+
}, 8, ["class"])]),
|
|
2827
|
+
_: 3
|
|
2828
|
+
}, 8, ["flat", "data-testid"])]),
|
|
2829
|
+
_: 3
|
|
2830
|
+
}, 8, ["loading"]);
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
});
|
|
2834
|
+
//#endregion
|
|
2835
|
+
//#region src/components/img/img.vue
|
|
2836
|
+
var img_default = /* @__PURE__ */ defineComponent({
|
|
2837
|
+
inheritAttrs: false,
|
|
2838
|
+
__name: "img",
|
|
2839
|
+
props: {
|
|
2840
|
+
src: {
|
|
2841
|
+
type: String,
|
|
2842
|
+
required: true
|
|
2843
|
+
},
|
|
2844
|
+
width: {
|
|
2845
|
+
type: [Number, String],
|
|
2846
|
+
required: false
|
|
2847
|
+
},
|
|
2848
|
+
height: {
|
|
2849
|
+
type: [Number, String],
|
|
2850
|
+
required: false
|
|
2851
|
+
},
|
|
2852
|
+
aspectRatio: {
|
|
2853
|
+
type: Number,
|
|
2854
|
+
required: false
|
|
2855
|
+
},
|
|
2856
|
+
cover: {
|
|
2857
|
+
type: Boolean,
|
|
2858
|
+
required: false
|
|
2859
|
+
},
|
|
2860
|
+
eager: {
|
|
2861
|
+
type: Boolean,
|
|
2862
|
+
required: false
|
|
2863
|
+
},
|
|
2864
|
+
dataTestid: {
|
|
2865
|
+
type: String,
|
|
2866
|
+
required: false
|
|
2867
|
+
}
|
|
2868
|
+
},
|
|
2869
|
+
setup(__props) {
|
|
2870
|
+
const props = __props;
|
|
2871
|
+
return (_ctx, _cache) => {
|
|
2872
|
+
return openBlock(), createBlock(unref(VImg), {
|
|
2873
|
+
src: props.src,
|
|
2874
|
+
alt: "",
|
|
2875
|
+
width: props.width,
|
|
2876
|
+
height: props.height,
|
|
2877
|
+
"aspect-ratio": props.aspectRatio,
|
|
2878
|
+
cover: props.cover,
|
|
2879
|
+
eager: props.eager,
|
|
2880
|
+
"data-testid": props.dataTestid
|
|
2881
|
+
}, null, 8, [
|
|
2882
|
+
"src",
|
|
2883
|
+
"width",
|
|
2884
|
+
"height",
|
|
2885
|
+
"aspect-ratio",
|
|
2886
|
+
"cover",
|
|
2887
|
+
"eager",
|
|
2888
|
+
"data-testid"
|
|
2889
|
+
]);
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
});
|
|
2893
|
+
//#endregion
|
|
2894
|
+
//#region src/components/link/link.vue
|
|
2895
|
+
var link_default = /* @__PURE__ */ defineComponent({
|
|
2896
|
+
inheritAttrs: false,
|
|
2897
|
+
__name: "link",
|
|
2898
|
+
props: {
|
|
2899
|
+
route: {
|
|
2900
|
+
type: null,
|
|
2901
|
+
required: true
|
|
2902
|
+
},
|
|
2903
|
+
dataTestid: {
|
|
2904
|
+
type: String,
|
|
2905
|
+
required: false
|
|
2906
|
+
}
|
|
2907
|
+
},
|
|
2908
|
+
setup(__props) {
|
|
2909
|
+
const props = __props;
|
|
2910
|
+
const routeProps = useRouteProps(() => props.route);
|
|
2911
|
+
return (_ctx, _cache) => {
|
|
2912
|
+
return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" No Vuetify component renders an inline text link, so the class removes the browser's underline "), (openBlock(), createBlock(resolveDynamicComponent(unref(isExternalRoute)(props.route) ? "a" : unref(RouterLink)), mergeProps(unref(routeProps), {
|
|
2913
|
+
class: "text-decoration-none",
|
|
2914
|
+
"data-testid": props.dataTestid
|
|
2915
|
+
}), {
|
|
2916
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
2917
|
+
_: 3
|
|
2918
|
+
}, 16, ["data-testid"]))], 2112);
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
});
|
|
2922
|
+
//#endregion
|
|
2923
|
+
//#region src/composables/data-set/data-set.ts
|
|
2924
|
+
/**
|
|
2925
|
+
* Width, out of the grid's 12 columns, taken by each record so that
|
|
2926
|
+
* `columns` records fit side by side on a row.
|
|
2927
|
+
*/
|
|
2928
|
+
const dataSetColumnsToColumnCols = {
|
|
2929
|
+
1: "12",
|
|
2930
|
+
2: "6",
|
|
2931
|
+
3: "4",
|
|
2932
|
+
4: "3",
|
|
2933
|
+
6: "2"
|
|
2934
|
+
};
|
|
2935
|
+
/**
|
|
2936
|
+
* Space, in pixels, between two cards of the same row: the default gutter of
|
|
2937
|
+
* `URow`/`UColumn` (Vuetify's `v-row` with its default density puts a 24px
|
|
2938
|
+
* gap between its columns). `n` cards side by side take `n` card widths plus
|
|
2939
|
+
* `n - 1` gaps, so they fit in `width` pixels when
|
|
2940
|
+
* `n * (minDataSetItemWidth + gap) <= width + gap`.
|
|
2941
|
+
*/
|
|
2942
|
+
const dataSetItemGap = 24;
|
|
2943
|
+
/**
|
|
2944
|
+
* Supported numbers of records per row, from the largest to the smallest.
|
|
2945
|
+
*/
|
|
2946
|
+
const dataSetColumnsOptions = [
|
|
2947
|
+
6,
|
|
2948
|
+
4,
|
|
2949
|
+
3,
|
|
2950
|
+
2,
|
|
2951
|
+
1
|
|
2952
|
+
];
|
|
2953
|
+
/**
|
|
2954
|
+
* Number of records to place side by side: the largest supported amount, up
|
|
2955
|
+
* to `columns`, that keeps each card at least {@link minDataSetItemWidth}
|
|
2956
|
+
* pixels wide, besides the {@link dataSetItemGap} between cards, in a data set
|
|
2957
|
+
* `width` pixels wide, but never less than one.
|
|
2958
|
+
* While the width is unknown (`0`, e.g. before the first measure or during
|
|
2959
|
+
* server-side rendering), `columns` is used as is.
|
|
2960
|
+
*/
|
|
2961
|
+
function getDataSetFittingColumns(columns, width) {
|
|
2962
|
+
if (!width) return columns;
|
|
2963
|
+
const fittingColumns = Math.min(columns, Math.floor((width + dataSetItemGap) / 264));
|
|
2964
|
+
return dataSetColumnsOptions.find((option) => option <= fittingColumns) ?? 1;
|
|
2965
|
+
}
|
|
2966
|
+
/**
|
|
2967
|
+
* Reactive version of {@link getDataSetFittingColumns}.
|
|
2968
|
+
*/
|
|
2969
|
+
function useDataSetFittingColumns(columns, width) {
|
|
2970
|
+
return computed(() => getDataSetFittingColumns(toValue(columns), toValue(width)));
|
|
2971
|
+
}
|
|
2972
|
+
/**
|
|
2973
|
+
* Width, in pixels, of the given element, kept up to date with a
|
|
2974
|
+
* `ResizeObserver`. It's `0` while there's no element, e.g. before it's
|
|
2975
|
+
* mounted, and the observer is disconnected when the element changes or the
|
|
2976
|
+
* calling scope is disposed.
|
|
2977
|
+
*/
|
|
2978
|
+
function useDataSetWidth(element) {
|
|
2979
|
+
const width = ref(0);
|
|
2980
|
+
let observer;
|
|
2981
|
+
function disconnect() {
|
|
2982
|
+
observer?.disconnect();
|
|
2983
|
+
observer = void 0;
|
|
2984
|
+
}
|
|
2985
|
+
watch(() => toValue(element), (currentElement) => {
|
|
2986
|
+
disconnect();
|
|
2987
|
+
width.value = 0;
|
|
2988
|
+
if (!currentElement) return;
|
|
2989
|
+
observer = new ResizeObserver(([entry]) => {
|
|
2990
|
+
width.value = entry.contentRect.width;
|
|
2991
|
+
});
|
|
2992
|
+
observer.observe(currentElement);
|
|
2993
|
+
}, { immediate: true });
|
|
2994
|
+
onScopeDispose(disconnect);
|
|
2995
|
+
return width;
|
|
2996
|
+
}
|
|
2997
|
+
/**
|
|
2998
|
+
* Maps the number of records per row to the width of each record's grid
|
|
2999
|
+
* column.
|
|
3000
|
+
*/
|
|
3001
|
+
function useDataSetColumnCols(columns) {
|
|
3002
|
+
return computed(() => dataSetColumnsToColumnCols[toValue(columns)]);
|
|
3003
|
+
}
|
|
3004
|
+
/**
|
|
3005
|
+
* Maps the record properties considered by the search to Vuetify's filter
|
|
3006
|
+
* keys. An empty list means every property, just like an undefined one.
|
|
3007
|
+
*/
|
|
3008
|
+
function useDataSetFilterKeys(searchKeys) {
|
|
3009
|
+
return computed(() => {
|
|
3010
|
+
const keys = toValue(searchKeys);
|
|
3011
|
+
return keys?.length ? keys : void 0;
|
|
3012
|
+
});
|
|
3013
|
+
}
|
|
3014
|
+
/**
|
|
3015
|
+
* Up to this many pages, every page button is shown. Vuetify's automatic
|
|
3016
|
+
* sizing collapses 3 pages into `1 … 2 … 3` in containers narrower than
|
|
3017
|
+
* ~336px, although showing every page never takes more room than its
|
|
3018
|
+
* collapsed form (`1 … x … 5`, 5 buttons) up to this amount.
|
|
3019
|
+
*/
|
|
3020
|
+
const maxAlwaysVisiblePages = 5;
|
|
3021
|
+
/**
|
|
3022
|
+
* Number of page buttons to show for `pageCount` pages: every page up to
|
|
3023
|
+
* {@link maxAlwaysVisiblePages}, otherwise `undefined` to let Vuetify fit them
|
|
3024
|
+
* to the available width.
|
|
3025
|
+
*/
|
|
3026
|
+
function getDataSetTotalVisible(pageCount) {
|
|
3027
|
+
return pageCount <= maxAlwaysVisiblePages ? pageCount : void 0;
|
|
3028
|
+
}
|
|
3029
|
+
/**
|
|
3030
|
+
* Goes back to the first page whenever the search changes. It must run before
|
|
3031
|
+
* the new search reaches Vuetify's iterator, which would otherwise clamp the
|
|
3032
|
+
* outdated page to the last one of the filtered records.
|
|
3033
|
+
*/
|
|
3034
|
+
function useDataSetSearchPageReset(search, page) {
|
|
3035
|
+
watch(() => toValue(search), () => {
|
|
3036
|
+
page.value = 1;
|
|
3037
|
+
});
|
|
3038
|
+
}
|
|
3039
|
+
//#endregion
|
|
3040
|
+
//#region src/components/data-sets/data-set/data-set.vue
|
|
3041
|
+
var data_set_default = /* @__PURE__ */ defineComponent({
|
|
3042
|
+
inheritAttrs: false,
|
|
3043
|
+
__name: "data-set",
|
|
3044
|
+
props: /*@__PURE__*/ mergeModels({
|
|
3045
|
+
items: {
|
|
3046
|
+
type: Array,
|
|
3047
|
+
required: false
|
|
3048
|
+
},
|
|
3049
|
+
columns: {
|
|
3050
|
+
type: Number,
|
|
3051
|
+
required: false,
|
|
3052
|
+
default: 3
|
|
3053
|
+
},
|
|
3054
|
+
itemsPerPage: {
|
|
3055
|
+
type: Number,
|
|
3056
|
+
required: false,
|
|
3057
|
+
default: 10
|
|
3058
|
+
},
|
|
3059
|
+
searchable: {
|
|
3060
|
+
type: Boolean,
|
|
3061
|
+
required: false
|
|
3062
|
+
},
|
|
3063
|
+
searchKeys: {
|
|
3064
|
+
type: Array,
|
|
3065
|
+
required: false
|
|
3066
|
+
},
|
|
3067
|
+
noDataText: {
|
|
3068
|
+
type: String,
|
|
3069
|
+
required: false,
|
|
3070
|
+
default: "Nenhum registro encontrado."
|
|
3071
|
+
},
|
|
3072
|
+
loading: {
|
|
3073
|
+
type: Boolean,
|
|
3074
|
+
required: false
|
|
3075
|
+
},
|
|
3076
|
+
dataTestid: {
|
|
3077
|
+
type: String,
|
|
3078
|
+
required: false
|
|
3079
|
+
}
|
|
3080
|
+
}, {
|
|
3081
|
+
"page": {
|
|
3082
|
+
type: Number,
|
|
3083
|
+
default: 1
|
|
3084
|
+
},
|
|
3085
|
+
"pageModifiers": {},
|
|
3086
|
+
"search": {
|
|
3087
|
+
type: String,
|
|
3088
|
+
default: ""
|
|
3089
|
+
},
|
|
3090
|
+
"searchModifiers": {}
|
|
3091
|
+
}),
|
|
3092
|
+
emits: ["update:page", "update:search"],
|
|
3093
|
+
setup(__props) {
|
|
3094
|
+
const props = __props;
|
|
3095
|
+
useSlots();
|
|
3096
|
+
const page = useModel(__props, "page");
|
|
3097
|
+
const search = useModel(__props, "search");
|
|
3098
|
+
const iterator = useTemplateRef("iterator");
|
|
3099
|
+
const columnCols = useDataSetColumnCols(useDataSetFittingColumns(() => props.columns, useDataSetWidth(() => iterator.value?.$el)));
|
|
3100
|
+
const filterKeys = useDataSetFilterKeys(() => props.searchKeys);
|
|
3101
|
+
useDataSetSearchPageReset(search, page);
|
|
3102
|
+
return (_ctx, _cache) => {
|
|
3103
|
+
return openBlock(), createBlock(skeleton_loader_default, {
|
|
3104
|
+
loading: props.loading,
|
|
3105
|
+
type: "card",
|
|
3106
|
+
"full-width": ""
|
|
3107
|
+
}, {
|
|
3108
|
+
default: withCtx(() => [createVNode(unref(VDataIterator), {
|
|
3109
|
+
ref_key: "iterator",
|
|
3110
|
+
ref: iterator,
|
|
3111
|
+
page: page.value,
|
|
3112
|
+
"onUpdate:page": _cache[2] || (_cache[2] = ($event) => page.value = $event),
|
|
3113
|
+
items: props.items,
|
|
3114
|
+
"items-per-page": props.itemsPerPage,
|
|
3115
|
+
search: search.value,
|
|
3116
|
+
"filter-keys": unref(filterKeys),
|
|
3117
|
+
"data-testid": props.dataTestid
|
|
3118
|
+
}, {
|
|
3119
|
+
header: withCtx(() => [props.searchable ? (openBlock(), createBlock(text_field_default, {
|
|
3120
|
+
key: 0,
|
|
3121
|
+
modelValue: search.value,
|
|
3122
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
|
|
3123
|
+
type: "search",
|
|
3124
|
+
label: "Pesquisar"
|
|
3125
|
+
}, null, 8, ["modelValue"])) : createCommentVNode("v-if", true)]),
|
|
3126
|
+
default: withCtx(({ items: pageItems }) => [createCommentVNode(" Stretched, so the items of the same line (e.g. full-height cards) share its height "), createVNode(row_default, {
|
|
3127
|
+
list: "",
|
|
3128
|
+
"align-y": "stretch"
|
|
3129
|
+
}, {
|
|
3130
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(pageItems, (pageItem, index) => {
|
|
3131
|
+
return openBlock(), createBlock(column_default, {
|
|
3132
|
+
key: index,
|
|
3133
|
+
cols: unref(columnCols),
|
|
3134
|
+
"list-item": ""
|
|
3135
|
+
}, {
|
|
3136
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default", {
|
|
3137
|
+
item: pageItem.raw,
|
|
3138
|
+
index
|
|
3139
|
+
})]),
|
|
3140
|
+
_: 2
|
|
3141
|
+
}, 1032, ["cols"]);
|
|
3142
|
+
}), 128))]),
|
|
3143
|
+
_: 2
|
|
3144
|
+
}, 1024)]),
|
|
3145
|
+
"no-data": withCtx(() => [createVNode(unref(VEmptyState), { text: props.noDataText }, null, 8, ["text"])]),
|
|
3146
|
+
footer: withCtx(({ pageCount }) => [pageCount > 1 ? (openBlock(), createBlock(unref(VPagination), {
|
|
3147
|
+
key: 0,
|
|
3148
|
+
modelValue: page.value,
|
|
3149
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => page.value = $event),
|
|
3150
|
+
length: pageCount,
|
|
3151
|
+
"total-visible": unref(getDataSetTotalVisible)(pageCount)
|
|
3152
|
+
}, null, 8, [
|
|
3153
|
+
"modelValue",
|
|
3154
|
+
"length",
|
|
3155
|
+
"total-visible"
|
|
3156
|
+
])) : createCommentVNode("v-if", true)]),
|
|
3157
|
+
_: 3
|
|
3158
|
+
}, 8, [
|
|
3159
|
+
"page",
|
|
3160
|
+
"items",
|
|
3161
|
+
"items-per-page",
|
|
3162
|
+
"search",
|
|
3163
|
+
"filter-keys",
|
|
3164
|
+
"data-testid"
|
|
3165
|
+
])]),
|
|
3166
|
+
_: 3
|
|
3167
|
+
}, 8, ["loading"]);
|
|
3168
|
+
};
|
|
3169
|
+
}
|
|
3170
|
+
});
|
|
3171
|
+
//#endregion
|
|
3172
|
+
//#region src/components/data-sets/data-set-item/data-set-item.vue
|
|
3173
|
+
var data_set_item_default = /* @__PURE__ */ defineComponent({
|
|
3174
|
+
inheritAttrs: false,
|
|
3175
|
+
__name: "data-set-item",
|
|
3176
|
+
props: {
|
|
3177
|
+
title: {
|
|
3178
|
+
type: String,
|
|
3179
|
+
required: false
|
|
3180
|
+
},
|
|
3181
|
+
subtitle: {
|
|
3182
|
+
type: String,
|
|
3183
|
+
required: false
|
|
3184
|
+
},
|
|
3185
|
+
variant: {
|
|
3186
|
+
type: String,
|
|
3187
|
+
required: false
|
|
3188
|
+
},
|
|
3189
|
+
actions: {
|
|
3190
|
+
type: Array,
|
|
3191
|
+
required: false
|
|
3192
|
+
},
|
|
3193
|
+
dataTestid: {
|
|
3194
|
+
type: String,
|
|
3195
|
+
required: false
|
|
3196
|
+
}
|
|
3197
|
+
},
|
|
3198
|
+
setup(__props) {
|
|
3199
|
+
const props = __props;
|
|
3200
|
+
const slots = useSlots();
|
|
3201
|
+
return (_ctx, _cache) => {
|
|
3202
|
+
return openBlock(), createBlock(section_default, {
|
|
3203
|
+
title: props.title,
|
|
3204
|
+
subtitle: props.subtitle,
|
|
3205
|
+
variant: props.variant,
|
|
3206
|
+
actions: props.actions,
|
|
3207
|
+
"data-testid": props.dataTestid,
|
|
3208
|
+
"full-height": ""
|
|
3209
|
+
}, {
|
|
3210
|
+
default: withCtx(() => [slots.default ? (openBlock(), createBlock(section_content_default, { key: 0 }, {
|
|
3211
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
3212
|
+
_: 3
|
|
3213
|
+
})) : createCommentVNode("v-if", true)]),
|
|
3214
|
+
_: 3
|
|
3215
|
+
}, 8, [
|
|
3216
|
+
"title",
|
|
3217
|
+
"subtitle",
|
|
3218
|
+
"variant",
|
|
3219
|
+
"actions",
|
|
3220
|
+
"data-testid"
|
|
3221
|
+
]);
|
|
3222
|
+
};
|
|
3223
|
+
}
|
|
3224
|
+
});
|
|
3225
|
+
//#endregion
|
|
3226
|
+
export { autocomplete_field_default as UAutocompleteField, button_default as UButton, checkbox_default as UCheckbox, column_default as UColumn, container_default as UContainer, data_set_default as UDataSet, data_set_item_default as UDataSetItem, date_time_field_default as UDateTimeField, details_default as UDetails, dialog_default as UDialog, form_default as UForm, icon_button_default as UIconButton, img_default as UImg, link_default as ULink, main_default as UMain, mask_field_default as UMaskField, row_default as URow, section_default as USection, section_content_default as USectionContent, table_default as UTable, text_field_default as UTextField };
|