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