@nexdom/uimed-vue 2.0.0-beta.2 → 2.0.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
- import { r as useVuetifyColor, t as messages } from "./use-toast-CACuYhK_.js";
2
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, isRef, mergeModels, mergeProps, openBlock, ref, renderList, renderSlot, toDisplayString, toValue, unref, useModel, withCtx, withModifiers } from "vue";
3
- import { VApp, VAppBar, VAppBarNavIcon, VAppBarTitle, VAvatar, VBadge, VBtn, VCard, VCardActions, VCardText, VCol, VContainer, VDivider, VForm, VIcon, VIconBtn, VImg, VList, VListGroup, VListItem, VListItemAction, VListSubheader, VMain, VMenu, VNavigationDrawer, VRow, VSkeletonLoader, VSnackbarQueue, VTable, VTextField } from "vuetify/components";
4
- import { formatDateTime, isEmail, isEmpty, isPhone, isUrl, toPeriodInterval } from "@nexdom/shared/utils";
1
+ import { a as messages, n as owners, r as requests, s as useVuetifyColor } from "./use-dialog-Ck2_ZW7T.js";
2
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, inject, isRef, mergeModels, mergeProps, normalizeClass, onMounted, onScopeDispose, onUnmounted, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toValue, unref, useId, useModel, useSlots, useTemplateRef, watch, withCtx, withModifiers } from "vue";
3
+ import { pt } from "vuetify/locale";
4
+ 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, VForm, VIcon, VIconBtn, VImg, VList, VListGroup, VListItem, VListItemAction, VListSubheader, VLocaleProvider, VMain, VMenu, VNavigationDrawer, VPagination, VRow, VSkeletonLoader, VSnackbarQueue, VTable, VTextField, VTimePicker } from "vuetify/components";
5
+ import { formatDateTime, isEmail, isEmpty, isPhone, isUrl } from "@nexdom/shared/utils";
5
6
  //#region src/composables/navigation/use-route-props.ts
6
7
  function useRouteProps(route) {
7
8
  return computed(() => routeToProps(route));
@@ -15,7 +16,7 @@ function isExternalRoute(route) {
15
16
  }
16
17
  //#endregion
17
18
  //#region src/components/app-bar/app-bar.vue?vue&type=script&setup=true&lang.ts
18
- const _hoisted_1$2 = { class: "opacity-60" };
19
+ const _hoisted_1$3 = { class: "opacity-60" };
19
20
  //#endregion
20
21
  //#region src/components/app-bar/app-bar.vue
21
22
  var app_bar_default = /* @__PURE__ */ defineComponent({
@@ -49,6 +50,10 @@ var app_bar_default = /* @__PURE__ */ defineComponent({
49
50
  logo: {
50
51
  type: String,
51
52
  required: false
53
+ },
54
+ loading: {
55
+ type: Boolean,
56
+ required: false
52
57
  }
53
58
  }, {
54
59
  "notificationsOpen": {
@@ -75,145 +80,153 @@ var app_bar_default = /* @__PURE__ */ defineComponent({
75
80
  const showNotifications = computed(() => !!props.notifications);
76
81
  const unreadCount = computed(() => props.notifications.filter((notification) => !notification.read).length);
77
82
  const hasUnreadNotifications = computed(() => unreadCount.value > 0);
83
+ const unreadLabel = computed(() => unreadCount.value === 1 ? "1 não lida" : `${unreadCount.value} não lidas`);
84
+ const notificationsLabel = computed(() => hasUnreadNotifications.value ? `Notificações (${unreadLabel.value})` : "Notificações");
85
+ watch(() => props.loading, (loading) => {
86
+ if (loading) notificationsOpen.value = false;
87
+ });
78
88
  const showUser = computed(() => !!props.user);
79
89
  const userAvatarProps = computed(() => props.user.img ? { image: props.user.img } : { icon: "mdi-account-outline" });
80
90
  function isParentOption(option) {
81
91
  return "items" in option;
82
92
  }
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
93
  return (_ctx, _cache) => {
101
- return openBlock(), createBlock(unref(VAppBar), { "data-testid": props.dataTestid }, {
102
- append: withCtx(() => [
103
- showHelp.value ? (openBlock(), createBlock(unref(VBtn), mergeProps({ key: 0 }, unref(routeProps), {
104
- "data-testid": helpBtnDataTestid.value,
105
- icon: "mdi-help-circle-outline"
106
- }), null, 16, ["data-testid"])) : createCommentVNode("v-if", true),
107
- showNotifications.value ? (openBlock(), createBlock(unref(VMenu), {
108
- key: 1,
109
- modelValue: notificationsOpen.value,
110
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => notificationsOpen.value = $event),
111
- "close-on-content-click": false
112
- }, {
113
- activator: withCtx(({ props }) => [createVNode(unref(VBtn), mergeProps(props, {
114
- "data-testid": notificationsBtnDataTestid.value,
115
- icon: ""
116
- }), {
117
- default: withCtx(() => [createVNode(unref(VBadge), {
118
- "model-value": hasUnreadNotifications.value,
119
- content: unreadCount.value,
120
- color: "error"
121
- }, {
122
- default: withCtx(() => [createVNode(unref(VIcon), { icon: "mdi-bell-outline" })]),
123
- _: 1
124
- }, 8, ["model-value", "content"])]),
125
- _: 1
126
- }, 16, ["data-testid"])]),
127
- default: withCtx(() => [createVNode(unref(VCard), { "data-testid": notificationsMenuDataTestid.value }, {
128
- default: withCtx(() => [props.notifications?.length ? (openBlock(), createBlock(unref(VList), { key: 0 }, {
129
- default: withCtx(() => [createVNode(unref(VListSubheader), null, {
130
- default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode("Notificações", -1)])]),
94
+ return openBlock(), createBlock(unref(VAppBar), {
95
+ "data-testid": props.dataTestid,
96
+ "aria-busy": props.loading || void 0
97
+ }, {
98
+ append: withCtx(() => [createVNode(unref(VSkeletonLoader), {
99
+ loading: props.loading,
100
+ type: "avatar"
101
+ }, {
102
+ default: withCtx(() => [
103
+ showHelp.value ? (openBlock(), createBlock(unref(VBtn), mergeProps({ key: 0 }, unref(routeProps), {
104
+ "data-testid": helpBtnDataTestid.value,
105
+ "aria-label": "Ajuda",
106
+ icon: "mdi-help-circle-outline"
107
+ }), null, 16, ["data-testid"])) : createCommentVNode("v-if", true),
108
+ showNotifications.value ? (openBlock(), createBlock(unref(VMenu), {
109
+ key: 1,
110
+ modelValue: notificationsOpen.value,
111
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => notificationsOpen.value = $event),
112
+ "close-on-content-click": false
113
+ }, {
114
+ activator: withCtx(({ props }) => [createVNode(unref(VBtn), mergeProps(props, {
115
+ "data-testid": notificationsBtnDataTestid.value,
116
+ "aria-label": notificationsLabel.value,
117
+ icon: ""
118
+ }), {
119
+ default: withCtx(() => [createVNode(unref(VBadge), {
120
+ "model-value": hasUnreadNotifications.value,
121
+ content: unreadCount.value,
122
+ label: unreadLabel.value,
123
+ color: "error"
124
+ }, {
125
+ default: withCtx(() => [createVNode(unref(VIcon), { icon: "mdi-bell-outline" })]),
131
126
  _: 1
132
- }), (openBlock(true), createElementBlock(Fragment, null, renderList(props.notifications, (notification, index) => {
133
- return openBlock(), createBlock(unref(VListItem), {
134
- key: index,
135
- active: !notification.read,
136
- title: notification.title,
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))]),
127
+ }, 8, [
128
+ "model-value",
129
+ "content",
130
+ "label"
131
+ ])]),
152
132
  _: 1
153
- })) : (openBlock(), createBlock(unref(VCardText), { key: 1 }, {
154
- default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode("Nenhuma notificação", -1)])]),
133
+ }, 16, ["data-testid", "aria-label"])]),
134
+ default: withCtx(() => [createVNode(unref(VCard), { "data-testid": notificationsMenuDataTestid.value }, {
135
+ default: withCtx(() => [props.notifications?.length ? (openBlock(), createBlock(unref(VList), { key: 0 }, {
136
+ default: withCtx(() => [createVNode(unref(VListSubheader), null, {
137
+ default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode("Notificações", -1)])]),
138
+ _: 1
139
+ }), (openBlock(true), createElementBlock(Fragment, null, renderList(props.notifications, (notification, index) => {
140
+ return openBlock(), createBlock(unref(VListItem), {
141
+ key: index,
142
+ active: !notification.read,
143
+ title: notification.title,
144
+ subtitle: notification.subtitle,
145
+ "active-class": "text-primary"
146
+ }, createSlots({ _: 2 }, [notification.when ? {
147
+ name: "append",
148
+ fn: withCtx(() => [createVNode(unref(VListItemAction), { end: "" }, {
149
+ default: withCtx(() => [createElementVNode("small", _hoisted_1$3, toDisplayString(notification.when), 1)]),
150
+ _: 2
151
+ }, 1024)]),
152
+ key: "0"
153
+ } : void 0]), 1032, [
154
+ "active",
155
+ "title",
156
+ "subtitle"
157
+ ]);
158
+ }), 128))]),
159
+ _: 1
160
+ })) : (openBlock(), createBlock(unref(VCardText), { key: 1 }, {
161
+ default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode("Nenhuma notificação", -1)])]),
162
+ _: 1
163
+ }))]),
155
164
  _: 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)]),
165
+ }, 8, ["data-testid"])]),
170
166
  _: 1
171
- }, 16, ["data-testid"])]),
172
- default: withCtx(() => [createVNode(unref(VCard), {
173
- title: __props.user?.title,
174
- subtitle: __props.user?.subtitle,
175
- "data-testid": userMenuDataTestid.value
167
+ }, 8, ["modelValue"])) : createCommentVNode("v-if", true),
168
+ showUser.value ? (openBlock(), createBlock(unref(VMenu), {
169
+ key: 2,
170
+ "close-on-content-click": false
176
171
  }, {
177
- prepend: withCtx(() => [createVNode(unref(VAvatar), mergeProps(userAvatarProps.value, { color: "primary" }), null, 16)]),
178
- default: withCtx(() => [createVNode(unref(VDivider)), createVNode(unref(VList), { nav: "" }, {
179
- default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.user?.options, (option, index) => {
180
- return openBlock(), createElementBlock(Fragment, { key: index }, [index > 0 ? (openBlock(), createBlock(unref(VDivider), {
181
- key: 0,
182
- class: "mt-1"
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))]),
172
+ activator: withCtx(({ props }) => [createVNode(unref(VBtn), mergeProps(props, {
173
+ "data-testid": userBtnDataTestid.value,
174
+ "aria-label": "Menu do usuário",
175
+ icon: ""
176
+ }), {
177
+ default: withCtx(() => [createVNode(unref(VAvatar), mergeProps(userAvatarProps.value, { color: "primary" }), null, 16)]),
178
+ _: 1
179
+ }, 16, ["data-testid"])]),
180
+ default: withCtx(() => [createVNode(unref(VCard), {
181
+ title: __props.user?.title,
182
+ subtitle: __props.user?.subtitle,
183
+ "data-testid": userMenuDataTestid.value
184
+ }, {
185
+ prepend: withCtx(() => [createVNode(unref(VAvatar), mergeProps(userAvatarProps.value, { color: "primary" }), null, 16)]),
186
+ default: withCtx(() => [createVNode(unref(VDivider)), createVNode(unref(VList), { nav: "" }, {
187
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.user?.options, (option, index) => {
188
+ return openBlock(), createElementBlock(Fragment, { key: index }, [index > 0 ? (openBlock(), createBlock(unref(VDivider), {
189
+ key: 0,
190
+ class: "mt-1"
191
+ })) : createCommentVNode("v-if", true), isParentOption(option) ? (openBlock(), createBlock(unref(VListGroup), { key: 1 }, {
192
+ activator: withCtx(({ props }) => [createVNode(unref(VListItem), mergeProps({ ref_for: true }, props, { title: option.description }), null, 16, ["title"])]),
193
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(option.items, (childOption, index) => {
194
+ return openBlock(), createBlock(unref(VListItem), mergeProps({ key: index }, { ref_for: true }, unref(routeToProps)(childOption.route), {
195
+ title: childOption.description,
196
+ onClick: childOption.action
197
+ }), null, 16, ["title", "onClick"]);
198
+ }), 128))]),
199
+ _: 2
200
+ }, 1024)) : (openBlock(), createBlock(unref(VListItem), mergeProps({
201
+ key: 2,
202
+ title: option.description
203
+ }, { ref_for: true }, unref(routeToProps)(option.route), { onClick: option.action }), null, 16, ["title", "onClick"]))], 64);
204
+ }), 128))]),
205
+ _: 1
206
+ })]),
197
207
  _: 1
198
- })]),
208
+ }, 8, [
209
+ "title",
210
+ "subtitle",
211
+ "data-testid"
212
+ ])]),
199
213
  _: 1
200
- }, 8, [
201
- "title",
202
- "subtitle",
203
- "data-testid"
204
- ])]),
205
- _: 1
206
- })) : createCommentVNode("v-if", true)
207
- ]),
214
+ })) : createCommentVNode("v-if", true)
215
+ ]),
216
+ _: 1
217
+ }, 8, ["loading"])]),
208
218
  default: withCtx(() => [
209
219
  __props.navigation ? (openBlock(), createBlock(unref(VAppBarNavIcon), {
210
220
  key: 0,
211
221
  "data-testid": navigationBtnDataTestid.value,
222
+ "aria-expanded": navigationOpen.value,
223
+ "aria-label": "Menu de navegação",
212
224
  onClick: _cache[0] || (_cache[0] = ($event) => navigationOpen.value = !navigationOpen.value)
213
- }, null, 8, ["data-testid"])) : createCommentVNode("v-if", true),
225
+ }, null, 8, ["data-testid", "aria-expanded"])) : createCommentVNode("v-if", true),
214
226
  __props.logo ? (openBlock(), createBlock(unref(VImg), {
215
227
  key: 1,
216
228
  src: props.logo,
229
+ alt: "",
217
230
  height: "100%",
218
231
  width: "fit-content",
219
232
  class: "py-2 ml-5 mr-n3",
@@ -225,7 +238,7 @@ var app_bar_default = /* @__PURE__ */ defineComponent({
225
238
  })) : createCommentVNode("v-if", true)
226
239
  ]),
227
240
  _: 1
228
- }, 8, ["data-testid"]);
241
+ }, 8, ["data-testid", "aria-busy"]);
229
242
  };
230
243
  }
231
244
  });
@@ -244,7 +257,23 @@ function url(value) {
244
257
  return isEmpty(value) || isUrl(value) || "URL inválida";
245
258
  }
246
259
  //#endregion
247
- //#region src/composables/inputs/use-rules.ts
260
+ //#region src/composables/inputs/fields.ts
261
+ const textFieldVariantToVuetifyVariant = {
262
+ primary: "underlined",
263
+ secondary: "outlined"
264
+ };
265
+ function useTextFieldVariant(variant) {
266
+ return computed(() => {
267
+ const variantValue = toValue(variant);
268
+ return textFieldVariantToVuetifyVariant[variantValue];
269
+ });
270
+ }
271
+ function createRequiredRule(required, isPresent) {
272
+ return (value) => {
273
+ if (!required) return true;
274
+ return isPresent(value) || "Campo obrigatório";
275
+ };
276
+ }
248
277
  function useRules(options) {
249
278
  return computed(() => {
250
279
  const rules = [];
@@ -254,10 +283,6 @@ function useRules(options) {
254
283
  }
255
284
  //#endregion
256
285
  //#region src/composables/inputs/text-field.ts
257
- const textFieldVariantToVuetifyVariant = {
258
- primary: "underlined",
259
- secondary: "outlined"
260
- };
261
286
  const textFieldTypeToVuetifyType = {
262
287
  text: "text",
263
288
  phone: "tel",
@@ -271,12 +296,6 @@ const textFieldTypeToVuetifyRule = {
271
296
  email,
272
297
  url
273
298
  };
274
- function useTextFieldVariant(variant) {
275
- return computed(() => {
276
- const variantValue = toValue(variant);
277
- return textFieldVariantToVuetifyVariant[variantValue];
278
- });
279
- }
280
299
  function useTextFieldType(type) {
281
300
  return computed(() => {
282
301
  const typeValue = toValue(type);
@@ -405,7 +424,7 @@ var text_field_default = /* @__PURE__ */ defineComponent({
405
424
  });
406
425
  //#endregion
407
426
  //#region src/components/navigation-menu/navigation-menu.vue?vue&type=script&setup=true&lang.ts
408
- const _hoisted_1$1 = { class: "px-4 pt-2" };
427
+ const _hoisted_1$2 = { class: "px-4 pt-2" };
409
428
  //#endregion
410
429
  //#region src/components/navigation-menu/navigation-menu.vue
411
430
  var navigation_menu_default = /* @__PURE__ */ defineComponent({
@@ -419,6 +438,10 @@ var navigation_menu_default = /* @__PURE__ */ defineComponent({
419
438
  items: {
420
439
  type: Array,
421
440
  required: false
441
+ },
442
+ loading: {
443
+ type: Boolean,
444
+ required: false
422
445
  }
423
446
  }, {
424
447
  "modelValue": {
@@ -458,37 +481,55 @@ var navigation_menu_default = /* @__PURE__ */ defineComponent({
458
481
  return openBlock(), createBlock(unref(VNavigationDrawer), {
459
482
  modelValue: open.value,
460
483
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => open.value = $event),
484
+ color: "primary",
461
485
  "data-testid": props.dataTestid,
486
+ "aria-busy": props.loading || void 0,
462
487
  absolute: "",
463
488
  temporary: ""
464
489
  }, {
465
- prepend: withCtx(() => [createElementVNode("div", _hoisted_1$1, [createVNode(text_field_default, {
490
+ prepend: withCtx(() => [createElementVNode("div", _hoisted_1$2, [createVNode(text_field_default, {
466
491
  modelValue: search.value,
467
492
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
468
493
  type: "search",
469
494
  label: "Buscar",
470
- "data-testid": searchDataTestid.value
471
- }, null, 8, ["modelValue", "data-testid"])])]),
472
- default: withCtx(() => [createVNode(unref(VDivider)), createVNode(unref(VList), { nav: "" }, {
473
- default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(filteredItems.value, (item, index) => {
474
- return openBlock(), createElementBlock(Fragment, { key: index }, [isParentItem(item) ? (openBlock(), createBlock(unref(VListGroup), { key: 0 }, {
475
- activator: withCtx(({ props: activatorProps }) => [createVNode(unref(VListItem), mergeProps({ ref_for: true }, activatorProps, { title: item.description }), null, 16, ["title"])]),
476
- default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(item.items, (childItem, childIndex) => {
477
- return openBlock(), createBlock(unref(VListItem), mergeProps({ key: childIndex }, { ref_for: true }, unref(routeToProps)(childItem.route), {
478
- title: childItem.description,
479
- onClick: childItem.action
480
- }), null, 16, ["title", "onClick"]);
481
- }), 128))]),
482
- _: 2
483
- }, 1024)) : (openBlock(), createBlock(unref(VListItem), mergeProps({
484
- key: 1,
485
- title: item.description
486
- }, { ref_for: true }, unref(routeToProps)(item.route), { onClick: item.action }), null, 16, ["title", "onClick"]))], 64);
487
- }), 128))]),
495
+ "data-testid": searchDataTestid.value,
496
+ disabled: props.loading
497
+ }, null, 8, [
498
+ "modelValue",
499
+ "data-testid",
500
+ "disabled"
501
+ ])])]),
502
+ default: withCtx(() => [createVNode(unref(VDivider)), createVNode(unref(VSkeletonLoader), {
503
+ loading: props.loading,
504
+ type: "list-item@6",
505
+ color: "primary"
506
+ }, {
507
+ default: withCtx(() => [createVNode(unref(VList), { nav: "" }, {
508
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(filteredItems.value, (item, index) => {
509
+ return openBlock(), createElementBlock(Fragment, { key: index }, [isParentItem(item) ? (openBlock(), createBlock(unref(VListGroup), { key: 0 }, {
510
+ activator: withCtx(({ props: activatorProps }) => [createVNode(unref(VListItem), mergeProps({ ref_for: true }, activatorProps, { title: item.description }), null, 16, ["title"])]),
511
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(item.items, (childItem, childIndex) => {
512
+ return openBlock(), createBlock(unref(VListItem), mergeProps({ key: childIndex }, { ref_for: true }, unref(routeToProps)(childItem.route), {
513
+ title: childItem.description,
514
+ onClick: childItem.action
515
+ }), null, 16, ["title", "onClick"]);
516
+ }), 128))]),
517
+ _: 2
518
+ }, 1024)) : (openBlock(), createBlock(unref(VListItem), mergeProps({
519
+ key: 1,
520
+ title: item.description
521
+ }, { ref_for: true }, unref(routeToProps)(item.route), { onClick: item.action }), null, 16, ["title", "onClick"]))], 64);
522
+ }), 128))]),
523
+ _: 1
524
+ })]),
488
525
  _: 1
489
- })]),
526
+ }, 8, ["loading"])]),
490
527
  _: 1
491
- }, 8, ["modelValue", "data-testid"]);
528
+ }, 8, [
529
+ "modelValue",
530
+ "data-testid",
531
+ "aria-busy"
532
+ ]);
492
533
  };
493
534
  }
494
535
  });
@@ -543,76 +584,11 @@ var toast_default = /* @__PURE__ */ defineComponent({
543
584
  }
544
585
  });
545
586
  //#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"]);
607
- };
608
- }
609
- });
610
- //#endregion
611
587
  //#region src/composables/button/button.ts
612
588
  const buttonVariantToVuetifyVariant = {
613
589
  primary: "elevated",
614
590
  secondary: "flat",
615
- ghost: "outlined"
591
+ ghost: "text"
616
592
  };
617
593
  function useButtonVariant(variant) {
618
594
  return computed(() => {
@@ -700,61 +676,94 @@ var button_default = /* @__PURE__ */ defineComponent({
700
676
  }
701
677
  });
702
678
  //#endregion
703
- //#region src/components/grid/row/row.vue
704
- var row_default = /* @__PURE__ */ defineComponent({
705
- inheritAttrs: false,
706
- __name: "row",
707
- props: { dataTestid: {
708
- type: String,
709
- required: false
710
- } },
711
- setup(__props) {
712
- const props = __props;
713
- return (_ctx, _cache) => {
714
- return openBlock(), createBlock(unref(VRow), { "data-testid": props.dataTestid }, {
715
- default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
716
- _: 3
717
- }, 8, ["data-testid"]);
718
- };
679
+ //#region src/components/dialogs/dialog/dialog-host-key.ts
680
+ /**
681
+ * Injection key of {@link DialogHostContext}, provided by the internal `DialogHost`. Besides the
682
+ * role, the blocking while an action runs and the end of the leave transitions, it makes the
683
+ * dialog's content describe the dialog (`aria-describedby`), since `DialogHost` only displays text
684
+ * messages. Not part of the public API.
685
+ */
686
+ const dialogHostKey = Symbol("dialog-host");
687
+ //#endregion
688
+ //#region src/composables/dialogs/use-dialog-focus.ts
689
+ /** Focus state of the closed dialogs, by their card, for the dialogs opened from them. */
690
+ const closedDialogs = /* @__PURE__ */ new WeakMap();
691
+ /** Elements that can get the focus when the dialog opens. The first one in the dialog gets it. */
692
+ const focusable = [
693
+ "button:not([disabled])",
694
+ "[href]",
695
+ "input:not([disabled]):not([type=\"hidden\"])",
696
+ "select:not([disabled])",
697
+ "textarea:not([disabled])",
698
+ "[tabindex]:not([tabindex=\"-1\"])"
699
+ ].join(", ");
700
+ /**
701
+ * Moves the focus into a dialog once it opens and back to where it was when it closes, including
702
+ * when dialogs are opened from others. Internal to the dialog components.
703
+ * @param isActive - Whether the dialog is displayed. It turns `false` as soon as the dialog starts
704
+ * leaving, so the focus returns right away.
705
+ * @param card - The dialog's card, marked with `data-u-dialog`, or nothing while it isn't rendered.
706
+ * @returns `focusFirstElement`, to call once the dialog finishes entering.
707
+ */
708
+ function useDialogFocus(isActive, card) {
709
+ /** Focus state of this dialog, shared with the dialogs opened from it. */
710
+ const focusState = {
711
+ isOpen: isActive,
712
+ origin: null
713
+ };
714
+ onMounted(() => onActiveChange(isActive()));
715
+ watch(isActive, onActiveChange);
716
+ function onActiveChange(active) {
717
+ if (active) {
718
+ focusState.origin = document.activeElement;
719
+ return;
720
+ }
721
+ const element = card();
722
+ if (element) closedDialogs.set(element, focusState);
723
+ const target = findFocusTarget(focusState.origin);
724
+ if (hasFocus(element) && (target instanceof HTMLElement || target instanceof SVGElement)) target.focus();
719
725
  }
720
- });
726
+ function focusFirstElement() {
727
+ card()?.querySelector(focusable)?.focus();
728
+ }
729
+ return { focusFirstElement };
730
+ }
731
+ /**
732
+ * Element that gets the focus back when a dialog closes: its origin or, when the origin is inside
733
+ * another dialog that closed meanwhile (and can't get the focus anymore), that dialog's.
734
+ */
735
+ function findFocusTarget(origin) {
736
+ const card = origin?.closest("[data-u-dialog]");
737
+ const owner = card && closedDialogs.get(card);
738
+ return owner && !owner.isOpen() ? findFocusTarget(owner.origin) : origin;
739
+ }
740
+ /**
741
+ * Whether the focus is on the dialog or lost (on nothing or on the page's body), so it can move.
742
+ * Otherwise it moved elsewhere, e.g. to another dialog opened over this one.
743
+ */
744
+ function hasFocus(card) {
745
+ const active = document.activeElement;
746
+ if (!active) return true;
747
+ return !!card && (card.contains(active) || active.contains(card));
748
+ }
721
749
  //#endregion
722
- //#region src/components/grid/column/column.vue
723
- var column_default = /* @__PURE__ */ defineComponent({
750
+ //#region src/components/dialogs/dialog/dialog.vue?vue&type=script&setup=true&lang.ts
751
+ const _hoisted_1$1 = ["id"];
752
+ //#endregion
753
+ //#region src/components/dialogs/dialog/dialog.vue
754
+ var dialog_default = /* @__PURE__ */ defineComponent({
724
755
  inheritAttrs: false,
725
- __name: "column",
726
- props: {
727
- cols: {
756
+ __name: "dialog",
757
+ props: /*@__PURE__*/ mergeModels({
758
+ title: {
728
759
  type: String,
729
- required: false,
730
- default: "12"
760
+ required: false
731
761
  },
732
- dataTestid: {
733
- type: String,
762
+ actions: {
763
+ type: Array,
734
764
  required: false
735
- }
736
- },
737
- setup(__props) {
738
- const props = __props;
739
- return (_ctx, _cache) => {
740
- return openBlock(), createBlock(unref(VCol), {
741
- cols: "12",
742
- sm: props.cols,
743
- "data-testid": props.dataTestid
744
- }, {
745
- default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
746
- _: 3
747
- }, 8, ["sm", "data-testid"]);
748
- };
749
- }
750
- });
751
- //#endregion
752
- //#region src/components/form/form.vue
753
- var form_default = /* @__PURE__ */ defineComponent({
754
- inheritAttrs: false,
755
- __name: "form",
756
- props: {
757
- id: {
765
+ },
766
+ size: {
758
767
  type: String,
759
768
  required: false
760
769
  },
@@ -762,56 +771,873 @@ var form_default = /* @__PURE__ */ defineComponent({
762
771
  type: String,
763
772
  required: false
764
773
  }
765
- },
766
- emits: ["submit"],
767
- setup(__props, { emit: __emit }) {
774
+ }, {
775
+ "modelValue": {
776
+ type: Boolean,
777
+ default: false
778
+ },
779
+ "modelModifiers": {}
780
+ }),
781
+ emits: ["update:modelValue"],
782
+ setup(__props) {
768
783
  const props = __props;
769
- const emit = __emit;
770
- async function onSubmit(event) {
771
- const { valid } = await event;
772
- if (valid) emit("submit", event);
784
+ const model = useModel(__props, "modelValue");
785
+ const maxWidths = {
786
+ small: 400,
787
+ medium: 560,
788
+ large: 800
789
+ };
790
+ const host = inject(dialogHostKey, null);
791
+ const contentId = useId();
792
+ const card = useTemplateRef("card");
793
+ const title = computed(() => props.title || void 0);
794
+ const maxWidth = computed(() => maxWidths[props.size ?? "medium"]);
795
+ const role = computed(() => host?.role() ?? "dialog");
796
+ /**
797
+ * Whether the dialog is displayed. It follows `v-model`, but turns `false` as soon as the user
798
+ * closes the dialog, while `v-model` only does once the leave transition ends.
799
+ */
800
+ const isActive = ref(model.value);
801
+ watch(model, (value) => isActive.value = value);
802
+ const { focusFirstElement } = useDialogFocus(() => isActive.value, () => card.value?.$el);
803
+ function onAfterLeave() {
804
+ model.value = false;
805
+ host?.afterLeave();
773
806
  }
774
807
  return (_ctx, _cache) => {
775
- return openBlock(), createBlock(unref(VForm), {
776
- id: props.id,
808
+ return openBlock(), createBlock(unref(VDialog), {
809
+ modelValue: isActive.value,
810
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isActive.value = $event),
811
+ persistent: unref(host)?.persistent(),
812
+ "max-width": maxWidth.value,
813
+ role: role.value,
814
+ "aria-label": title.value,
815
+ "aria-labelledby": title.value ? void 0 : unref(contentId),
816
+ "aria-describedby": unref(host) ? unref(contentId) : void 0,
777
817
  "data-testid": props.dataTestid,
778
- onSubmit: withModifiers(onSubmit, ["prevent"])
818
+ scrollable: "",
819
+ onAfterEnter: unref(focusFirstElement),
820
+ onAfterLeave
779
821
  }, {
780
- default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
822
+ default: withCtx(() => [createCommentVNode(" `data-u-dialog` identifies the card to the dialogs opened from this one "), createVNode(unref(VCard), {
823
+ ref_key: "card",
824
+ ref: card,
825
+ title: title.value,
826
+ "data-u-dialog": ""
827
+ }, createSlots({
828
+ text: withCtx(() => [createElementVNode("div", { id: unref(contentId) }, [renderSlot(_ctx.$slots, "default")], 8, _hoisted_1$1)]),
829
+ _: 2
830
+ }, [props.actions?.length ? {
831
+ name: "actions",
832
+ fn: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.actions, ({ label, onClick, ...action }, index) => {
833
+ return openBlock(), createBlock(button_default, mergeProps({ key: index }, { ref_for: true }, action, { onClick }), {
834
+ default: withCtx(() => [createTextVNode(toDisplayString(label), 1)]),
835
+ _: 2
836
+ }, 1040, ["onClick"]);
837
+ }), 128))]),
838
+ key: "0"
839
+ } : void 0]), 1032, ["title"])]),
781
840
  _: 3
782
- }, 8, ["id", "data-testid"]);
841
+ }, 8, [
842
+ "modelValue",
843
+ "persistent",
844
+ "max-width",
845
+ "role",
846
+ "aria-label",
847
+ "aria-labelledby",
848
+ "aria-describedby",
849
+ "data-testid",
850
+ "onAfterEnter"
851
+ ]);
783
852
  };
784
853
  }
785
854
  });
786
855
  //#endregion
787
- //#region src/components/sections/section-content/section-content.vue
788
- var section_content_default = /* @__PURE__ */ defineComponent({
856
+ //#region src/components/dialogs/dialog-host.vue
857
+ var dialog_host_default = /* @__PURE__ */ defineComponent({
789
858
  inheritAttrs: false,
790
- __name: "section-content",
859
+ __name: "dialog-host",
791
860
  setup(__props) {
861
+ const owner = Symbol("dialog");
862
+ const isOwner = computed(() => owners.value[0] === owner);
863
+ const next = computed(() => isOwner.value ? requests.value[0] : void 0);
864
+ /** Dialog on display, kept until its leave transition ends. */
865
+ const request = shallowRef();
866
+ /**
867
+ * Changes for every dialog on display, so each one gets its own `Dialog`, open from the start.
868
+ * The next dialog can then open right after a dismissed one, whose `v-model` stays `true`.
869
+ */
870
+ const key = shallowRef();
871
+ const isOpen = ref(false);
872
+ const selected = shallowRef();
873
+ const isRunning = computed(() => selected.value !== void 0);
874
+ const actions = computed(() => {
875
+ const current = request.value;
876
+ if (!current) return void 0;
877
+ return current.actions.map((action) => ({
878
+ label: action.text,
879
+ variant: action.variant,
880
+ color: action.color,
881
+ loading: selected.value === action,
882
+ disabled: isRunning.value && selected.value !== action,
883
+ onClick: () => void select(current, action)
884
+ }));
885
+ });
886
+ provide(dialogHostKey, {
887
+ role: () => request.value?.role,
888
+ persistent: () => isRunning.value,
889
+ afterLeave: showNext
890
+ });
891
+ onMounted(() => owners.value.push(owner));
892
+ onUnmounted(() => owners.value = owners.value.filter((item) => item !== owner));
893
+ watch(next, show);
894
+ function show() {
895
+ if (request.value) return;
896
+ request.value = next.value;
897
+ key.value = Symbol();
898
+ isOpen.value = next.value !== void 0;
899
+ }
900
+ function showNext() {
901
+ request.value = void 0;
902
+ show();
903
+ }
904
+ async function select(current, action) {
905
+ if (isRunning.value) return;
906
+ requests.value.shift();
907
+ selected.value = action;
908
+ await current.select(action?.value);
909
+ selected.value = void 0;
910
+ if (action) isOpen.value = false;
911
+ }
912
+ /** Called once a dialog closed by the user leaves, right before the next one is displayed. */
913
+ function dismiss() {
914
+ if (request.value) select(request.value);
915
+ }
792
916
  return (_ctx, _cache) => {
793
- return openBlock(), createBlock(unref(VCardText), null, {
794
- default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
795
- _: 3
796
- });
917
+ return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" PascalCase, since `<dialog>` in the template would render the native HTML element instead "), (openBlock(), createBlock(dialog_default, {
918
+ key: key.value,
919
+ "model-value": isOpen.value,
920
+ title: request.value?.title,
921
+ actions: actions.value,
922
+ "onUpdate:modelValue": dismiss
923
+ }, {
924
+ default: withCtx(() => [createTextVNode(toDisplayString(request.value?.message), 1)]),
925
+ _: 1
926
+ }, 8, [
927
+ "model-value",
928
+ "title",
929
+ "actions"
930
+ ]))], 2112);
797
931
  };
798
932
  }
799
933
  });
800
934
  //#endregion
801
- //#region src/composables/section/section.ts
802
- const sectionVariantToVuetifyVariant = {
803
- primary: "elevated",
804
- secondary: "outlined"
805
- };
806
- function useSectionVariant(variant) {
807
- return computed(() => {
808
- const variantValue = toValue(variant);
809
- return variantValue ? sectionVariantToVuetifyVariant[variantValue] : sectionVariantToVuetifyVariant.primary;
810
- });
811
- }
812
- //#endregion
813
- //#region src/components/sections/section/section.vue
814
- var section_default = /* @__PURE__ */ defineComponent({
935
+ //#region src/components/main/main.vue
936
+ var main_default = /* @__PURE__ */ defineComponent({
937
+ inheritAttrs: false,
938
+ __name: "main",
939
+ props: /*@__PURE__*/ mergeModels({
940
+ dataTestid: {
941
+ type: String,
942
+ required: false
943
+ },
944
+ logo: {
945
+ type: String,
946
+ required: false
947
+ },
948
+ appBar: {
949
+ type: Object,
950
+ required: false
951
+ },
952
+ navigationMenu: {
953
+ type: Object,
954
+ required: false
955
+ },
956
+ loading: {
957
+ type: Boolean,
958
+ required: false
959
+ }
960
+ }, {
961
+ "notificationsOpen": {
962
+ type: Boolean,
963
+ default: false
964
+ },
965
+ "notificationsOpenModifiers": {}
966
+ }),
967
+ emits: ["update:notificationsOpen"],
968
+ setup(__props) {
969
+ const props = __props;
970
+ const appBarProps = computed(() => props.appBar ? {
971
+ ...props.appBar,
972
+ logo: props.logo,
973
+ navigation: props.navigationMenu !== void 0,
974
+ loading: props.loading
975
+ } : void 0);
976
+ const notificationsOpen = useModel(__props, "notificationsOpen");
977
+ const navigationMenuOpen = ref(false);
978
+ return (_ctx, _cache) => {
979
+ return openBlock(), createBlock(unref(VApp), { "data-testid": props.dataTestid }, {
980
+ default: withCtx(() => [
981
+ appBarProps.value ? (openBlock(), createBlock(app_bar_default, mergeProps({ key: 0 }, appBarProps.value, {
982
+ "notifications-open": notificationsOpen.value,
983
+ "onUpdate:notificationsOpen": _cache[0] || (_cache[0] = ($event) => notificationsOpen.value = $event),
984
+ "navigation-open": navigationMenuOpen.value,
985
+ "onUpdate:navigationOpen": _cache[1] || (_cache[1] = ($event) => navigationMenuOpen.value = $event)
986
+ }), null, 16, ["notifications-open", "navigation-open"])) : createCommentVNode("v-if", true),
987
+ props.navigationMenu ? (openBlock(), createBlock(navigation_menu_default, mergeProps({ key: 1 }, props.navigationMenu, {
988
+ modelValue: navigationMenuOpen.value,
989
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => navigationMenuOpen.value = $event),
990
+ loading: props.loading
991
+ }), null, 16, ["modelValue", "loading"])) : createCommentVNode("v-if", true),
992
+ createVNode(unref(VMain), null, {
993
+ default: withCtx(() => [createVNode(container_default, null, {
994
+ default: withCtx(() => [
995
+ renderSlot(_ctx.$slots, "default"),
996
+ createVNode(toast_default),
997
+ createVNode(dialog_host_default)
998
+ ]),
999
+ _: 3
1000
+ })]),
1001
+ _: 3
1002
+ })
1003
+ ]),
1004
+ _: 3
1005
+ }, 8, ["data-testid"]);
1006
+ };
1007
+ }
1008
+ });
1009
+ //#endregion
1010
+ //#region src/composables/inputs/autocomplete-field.ts
1011
+ function createAutocompleteRequiredRule(options) {
1012
+ return createRequiredRule(options.required, (value) => {
1013
+ if (options.multiple) return (Array.isArray(value) ? value : []).length > 0;
1014
+ return !isEmpty(String(value ?? ""));
1015
+ });
1016
+ }
1017
+ function useAutocompleteRules(options) {
1018
+ return computed(() => {
1019
+ const opts = toValue(options);
1020
+ const rules = [];
1021
+ if (opts.required) rules.push(createAutocompleteRequiredRule(opts));
1022
+ return rules;
1023
+ });
1024
+ }
1025
+ //#endregion
1026
+ //#region src/components/inputs/autocomplete-field/autocomplete-field.vue
1027
+ var autocomplete_field_default = /* @__PURE__ */ defineComponent({
1028
+ inheritAttrs: false,
1029
+ __name: "autocomplete-field",
1030
+ props: /*@__PURE__*/ mergeModels({
1031
+ variant: {
1032
+ type: String,
1033
+ required: false,
1034
+ default: "primary"
1035
+ },
1036
+ disabled: {
1037
+ type: Boolean,
1038
+ required: false
1039
+ },
1040
+ loading: {
1041
+ type: Boolean,
1042
+ required: false
1043
+ },
1044
+ clearable: {
1045
+ type: Boolean,
1046
+ required: false
1047
+ },
1048
+ readonly: {
1049
+ type: Boolean,
1050
+ required: false
1051
+ },
1052
+ required: {
1053
+ type: Boolean,
1054
+ required: false
1055
+ },
1056
+ label: {
1057
+ type: String,
1058
+ required: false
1059
+ },
1060
+ placeholder: {
1061
+ type: String,
1062
+ required: false
1063
+ },
1064
+ hint: {
1065
+ type: String,
1066
+ required: false
1067
+ },
1068
+ dataTestid: {
1069
+ type: String,
1070
+ required: false
1071
+ },
1072
+ strict: {
1073
+ type: Boolean,
1074
+ required: false
1075
+ },
1076
+ multiple: {
1077
+ type: null,
1078
+ required: false
1079
+ },
1080
+ items: {
1081
+ type: Array,
1082
+ required: false,
1083
+ default: () => []
1084
+ }
1085
+ }, {
1086
+ "modelValue": { type: null },
1087
+ "modelModifiers": {}
1088
+ }),
1089
+ emits: ["update:modelValue"],
1090
+ setup(__props) {
1091
+ const modelValue = useModel(__props, "modelValue");
1092
+ const props = __props;
1093
+ const vuetifyVariant = useTextFieldVariant(() => props.variant);
1094
+ const isMultiple = computed(() => props.multiple ?? false);
1095
+ const rules = useAutocompleteRules(() => ({
1096
+ required: props.required,
1097
+ multiple: isMultiple.value
1098
+ }));
1099
+ const vuetifyComponent = computed(() => props.strict ? VAutocomplete : VCombobox);
1100
+ const normalizedItems = computed(() => {
1101
+ return props.items.map((item) => {
1102
+ if (typeof item === "string") return {
1103
+ label: item,
1104
+ value: item
1105
+ };
1106
+ return item;
1107
+ });
1108
+ });
1109
+ return (_ctx, _cache) => {
1110
+ return openBlock(), createBlock(resolveDynamicComponent(vuetifyComponent.value), {
1111
+ modelValue: modelValue.value,
1112
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
1113
+ variant: unref(vuetifyVariant),
1114
+ items: normalizedItems.value,
1115
+ "data-testid": props.dataTestid,
1116
+ disabled: props.disabled,
1117
+ loading: props.loading,
1118
+ readonly: props.readonly,
1119
+ label: props.label,
1120
+ placeholder: props.placeholder,
1121
+ hint: props.hint,
1122
+ clearable: props.clearable,
1123
+ multiple: isMultiple.value,
1124
+ "item-title": "label",
1125
+ rules: unref(rules),
1126
+ chips: ""
1127
+ }, null, 8, [
1128
+ "modelValue",
1129
+ "variant",
1130
+ "items",
1131
+ "data-testid",
1132
+ "disabled",
1133
+ "loading",
1134
+ "readonly",
1135
+ "label",
1136
+ "placeholder",
1137
+ "hint",
1138
+ "clearable",
1139
+ "multiple",
1140
+ "rules"
1141
+ ]);
1142
+ };
1143
+ }
1144
+ });
1145
+ //#endregion
1146
+ //#region src/components/inputs/checkbox/checkbox.vue
1147
+ var checkbox_default = /* @__PURE__ */ defineComponent({
1148
+ inheritAttrs: false,
1149
+ __name: "checkbox",
1150
+ props: /*@__PURE__*/ mergeModels({
1151
+ label: {
1152
+ type: String,
1153
+ required: false
1154
+ },
1155
+ disabled: {
1156
+ type: Boolean,
1157
+ required: false
1158
+ },
1159
+ readonly: {
1160
+ type: Boolean,
1161
+ required: false
1162
+ },
1163
+ trueValue: {
1164
+ type: Boolean,
1165
+ required: false,
1166
+ skipCheck: true,
1167
+ default: true
1168
+ },
1169
+ falseValue: {
1170
+ type: Boolean,
1171
+ required: false,
1172
+ skipCheck: true,
1173
+ default: false
1174
+ },
1175
+ dataTestid: {
1176
+ type: String,
1177
+ required: false
1178
+ }
1179
+ }, {
1180
+ "modelValue": {
1181
+ type: null,
1182
+ default: (props) => props.falseValue
1183
+ },
1184
+ "modelModifiers": {}
1185
+ }),
1186
+ emits: ["update:modelValue"],
1187
+ setup(__props) {
1188
+ const props = __props;
1189
+ const modelValue = useModel(__props, "modelValue");
1190
+ return (_ctx, _cache) => {
1191
+ return openBlock(), createBlock(unref(VCheckboxBtn), {
1192
+ modelValue: modelValue.value,
1193
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
1194
+ "true-value": props.trueValue,
1195
+ "false-value": props.falseValue,
1196
+ "data-testid": props.dataTestid,
1197
+ disabled: props.disabled,
1198
+ readonly: props.readonly,
1199
+ label: props.label,
1200
+ class: "flex-0-1",
1201
+ "hide-details": ""
1202
+ }, null, 8, [
1203
+ "modelValue",
1204
+ "true-value",
1205
+ "false-value",
1206
+ "data-testid",
1207
+ "disabled",
1208
+ "readonly",
1209
+ "label"
1210
+ ]);
1211
+ };
1212
+ }
1213
+ });
1214
+ //#endregion
1215
+ //#region src/composables/inputs/date-time-field.ts
1216
+ /**
1217
+ * Extracts the date part (`YYYY-MM-DD`) of a value, or an empty string when
1218
+ * the type has no date.
1219
+ */
1220
+ function getDatePart(value, type) {
1221
+ return type === "time" ? "" : value.split("T")[0];
1222
+ }
1223
+ /**
1224
+ * Extracts the time part (`HH:mm`) of a value, or an empty string when the
1225
+ * value has no time.
1226
+ */
1227
+ function getTimePart(value, type) {
1228
+ if (type === "time") return value;
1229
+ const [, time = ""] = value.split("T");
1230
+ return time;
1231
+ }
1232
+ /**
1233
+ * Builds a value for the given type, or an empty string while any required
1234
+ * part is missing.
1235
+ */
1236
+ function joinDateTime(type, date, time) {
1237
+ if (type === "date") return date;
1238
+ if (type === "time") return time;
1239
+ return date && time ? `${date}T${time}` : "";
1240
+ }
1241
+ /**
1242
+ * Formats a value to be displayed in pt-BR, like `24/09/2026`, `14:30` or
1243
+ * `24/09/2026 14:30`.
1244
+ */
1245
+ function formatDateTime$1(value, type) {
1246
+ return [getDatePart(value, type).split("-").reverse().join("/"), getTimePart(value, type)].filter(Boolean).join(" ");
1247
+ }
1248
+ function useDateTimeFieldRules(props) {
1249
+ const defaultRules = useRules(props);
1250
+ return computed(() => {
1251
+ const rules = [...defaultRules.value];
1252
+ if (props.min) rules.push(minRule(props.min, props.type));
1253
+ if (props.max) rules.push(maxRule(props.max, props.type));
1254
+ return rules;
1255
+ });
1256
+ }
1257
+ /**
1258
+ * Computes the limits of each picker. For `datetime`, time limits only apply
1259
+ * when the selected date is the limit's own date.
1260
+ */
1261
+ function useDateTimeFieldPickerBounds(props, selectedDate) {
1262
+ return computed(() => {
1263
+ const min = props.min ?? "";
1264
+ const max = props.max ?? "";
1265
+ const date = toValue(selectedDate);
1266
+ return {
1267
+ dateMin: getDatePart(min, props.type),
1268
+ dateMax: getDatePart(max, props.type),
1269
+ timeMin: getTimeBound(min, props.type, date),
1270
+ timeMax: getTimeBound(max, props.type, date)
1271
+ };
1272
+ });
1273
+ }
1274
+ function getTimeBound(bound, type, selectedDate) {
1275
+ if (type === "datetime" && getDatePart(bound, type) !== selectedDate) return "";
1276
+ return getTimePart(bound, type);
1277
+ }
1278
+ function minRule(min, type) {
1279
+ const message = `Valor anterior ao mínimo permitido (${formatDateTime$1(min, type)})`;
1280
+ return (value) => !value || value >= min || message;
1281
+ }
1282
+ function maxRule(max, type) {
1283
+ const message = `Valor posterior ao máximo permitido (${formatDateTime$1(max, type)})`;
1284
+ return (value) => !value || value <= max || message;
1285
+ }
1286
+ //#endregion
1287
+ //#region src/components/inputs/date-time-field/date-time-field.vue
1288
+ var date_time_field_default = /* @__PURE__ */ defineComponent({
1289
+ inheritAttrs: false,
1290
+ __name: "date-time-field",
1291
+ props: /*@__PURE__*/ mergeModels({
1292
+ variant: {
1293
+ type: String,
1294
+ required: false,
1295
+ default: "primary"
1296
+ },
1297
+ type: {
1298
+ type: String,
1299
+ required: false,
1300
+ default: "datetime"
1301
+ },
1302
+ min: {
1303
+ type: String,
1304
+ required: false
1305
+ },
1306
+ max: {
1307
+ type: String,
1308
+ required: false
1309
+ },
1310
+ disabled: {
1311
+ type: Boolean,
1312
+ required: false
1313
+ },
1314
+ loading: {
1315
+ type: Boolean,
1316
+ required: false
1317
+ },
1318
+ clearable: {
1319
+ type: Boolean,
1320
+ required: false
1321
+ },
1322
+ readonly: {
1323
+ type: Boolean,
1324
+ required: false
1325
+ },
1326
+ required: {
1327
+ type: Boolean,
1328
+ required: false
1329
+ },
1330
+ label: {
1331
+ type: String,
1332
+ required: false
1333
+ },
1334
+ placeholder: {
1335
+ type: String,
1336
+ required: false
1337
+ },
1338
+ hint: {
1339
+ type: String,
1340
+ required: false
1341
+ },
1342
+ dataTestid: {
1343
+ type: String,
1344
+ required: false
1345
+ }
1346
+ }, {
1347
+ "modelValue": {
1348
+ type: String,
1349
+ default: ""
1350
+ },
1351
+ "modelModifiers": {}
1352
+ }),
1353
+ emits: ["update:modelValue"],
1354
+ setup(__props) {
1355
+ const modelValue = useModel(__props, "modelValue");
1356
+ const props = __props;
1357
+ const messages = { "pt-BR": pt };
1358
+ const vuetifyVariant = useTextFieldVariant(() => props.variant);
1359
+ const rules = useDateTimeFieldRules(props);
1360
+ const displayValue = computed(() => formatDateTime$1(modelValue.value, props.type));
1361
+ const isClearable = computed(() => props.clearable && !props.readonly);
1362
+ const isMenuDisabled = computed(() => props.disabled || props.readonly);
1363
+ const isMenuOpen = ref(false);
1364
+ const draft = ref({});
1365
+ const draftDate = computed(() => draft.value.date ?? getDatePart(modelValue.value, props.type));
1366
+ const draftTime = computed(() => draft.value.time ?? getTimePart(modelValue.value, props.type));
1367
+ const draftValue = computed(() => joinDateTime(props.type, draftDate.value, draftTime.value));
1368
+ const bounds = useDateTimeFieldPickerBounds(props, draftDate);
1369
+ watch(isMenuOpen, () => {
1370
+ draft.value = {};
1371
+ });
1372
+ function onDateUpdate(date) {
1373
+ draft.value.date = formatDateTime(date, "YYYY-MM-DD");
1374
+ commitDraft();
1375
+ closeIfComplete();
1376
+ }
1377
+ function onTimeUpdate(time) {
1378
+ draft.value.time = time ?? "";
1379
+ commitDraft();
1380
+ }
1381
+ function commitDraft() {
1382
+ if (draftValue.value) modelValue.value = draftValue.value;
1383
+ }
1384
+ function closeIfComplete() {
1385
+ if (draftValue.value) isMenuOpen.value = false;
1386
+ }
1387
+ function clear() {
1388
+ modelValue.value = "";
1389
+ }
1390
+ return (_ctx, _cache) => {
1391
+ return openBlock(), createBlock(unref(VTextField), {
1392
+ "model-value": displayValue.value,
1393
+ "validation-value": modelValue.value,
1394
+ variant: unref(vuetifyVariant),
1395
+ rules: unref(rules),
1396
+ "data-testid": props.dataTestid,
1397
+ disabled: props.disabled,
1398
+ loading: props.loading,
1399
+ label: props.label,
1400
+ placeholder: props.placeholder,
1401
+ hint: props.hint,
1402
+ clearable: isClearable.value,
1403
+ readonly: "",
1404
+ "onClick:clear": clear
1405
+ }, {
1406
+ default: withCtx(() => [createVNode(unref(VMenu), {
1407
+ modelValue: isMenuOpen.value,
1408
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isMenuOpen.value = $event),
1409
+ activator: "parent",
1410
+ location: "bottom start",
1411
+ "min-width": "0",
1412
+ disabled: isMenuDisabled.value,
1413
+ "close-on-content-click": false
1414
+ }, {
1415
+ default: withCtx(() => [createVNode(unref(VCard), null, {
1416
+ default: withCtx(() => [createVNode(unref(VLocaleProvider), {
1417
+ locale: "pt-BR",
1418
+ messages,
1419
+ class: "d-flex flex-wrap"
1420
+ }, {
1421
+ default: withCtx(() => [props.type !== "time" ? (openBlock(), createBlock(unref(VDatePicker), {
1422
+ key: 0,
1423
+ "model-value": draftDate.value || null,
1424
+ min: unref(bounds).dateMin,
1425
+ max: unref(bounds).dateMax,
1426
+ "onUpdate:modelValue": onDateUpdate
1427
+ }, null, 8, [
1428
+ "model-value",
1429
+ "min",
1430
+ "max"
1431
+ ])) : createCommentVNode("v-if", true), props.type !== "date" ? (openBlock(), createBlock(unref(VTimePicker), {
1432
+ key: 1,
1433
+ "model-value": draftTime.value,
1434
+ min: unref(bounds).timeMin,
1435
+ max: unref(bounds).timeMax,
1436
+ format: "24hr",
1437
+ "onUpdate:modelValue": onTimeUpdate,
1438
+ "onUpdate:minute": closeIfComplete
1439
+ }, null, 8, [
1440
+ "model-value",
1441
+ "min",
1442
+ "max"
1443
+ ])) : createCommentVNode("v-if", true)]),
1444
+ _: 1
1445
+ })]),
1446
+ _: 1
1447
+ })]),
1448
+ _: 1
1449
+ }, 8, ["modelValue", "disabled"])]),
1450
+ _: 1
1451
+ }, 8, [
1452
+ "model-value",
1453
+ "validation-value",
1454
+ "variant",
1455
+ "rules",
1456
+ "data-testid",
1457
+ "disabled",
1458
+ "loading",
1459
+ "label",
1460
+ "placeholder",
1461
+ "hint",
1462
+ "clearable"
1463
+ ]);
1464
+ };
1465
+ }
1466
+ });
1467
+ //#endregion
1468
+ //#region src/components/grid/row/row.vue
1469
+ var row_default = /* @__PURE__ */ defineComponent({
1470
+ inheritAttrs: false,
1471
+ __name: "row",
1472
+ props: {
1473
+ align: {
1474
+ type: String,
1475
+ required: false,
1476
+ default: "start"
1477
+ },
1478
+ list: {
1479
+ type: Boolean,
1480
+ required: false
1481
+ },
1482
+ dataTestid: {
1483
+ type: String,
1484
+ required: false
1485
+ }
1486
+ },
1487
+ setup(__props) {
1488
+ const props = __props;
1489
+ const rowAlignToClass = {
1490
+ start: "align-start",
1491
+ center: "align-center",
1492
+ end: "align-end",
1493
+ stretch: "align-stretch"
1494
+ };
1495
+ const alignClass = computed(() => rowAlignToClass[props.align]);
1496
+ const tag = computed(() => props.list ? "ul" : void 0);
1497
+ const listClasses = computed(() => props.list ? "pa-0 ma-0" : void 0);
1498
+ return (_ctx, _cache) => {
1499
+ return openBlock(), createBlock(unref(VRow), {
1500
+ tag: tag.value,
1501
+ class: normalizeClass([alignClass.value, listClasses.value]),
1502
+ "data-testid": props.dataTestid
1503
+ }, {
1504
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
1505
+ _: 3
1506
+ }, 8, [
1507
+ "tag",
1508
+ "class",
1509
+ "data-testid"
1510
+ ]);
1511
+ };
1512
+ }
1513
+ });
1514
+ //#endregion
1515
+ //#region src/components/grid/column/column.vue
1516
+ var column_default = /* @__PURE__ */ defineComponent({
1517
+ inheritAttrs: false,
1518
+ __name: "column",
1519
+ props: {
1520
+ cols: {
1521
+ type: String,
1522
+ required: false,
1523
+ default: "12"
1524
+ },
1525
+ listItem: {
1526
+ type: Boolean,
1527
+ required: false
1528
+ },
1529
+ dataTestid: {
1530
+ type: String,
1531
+ required: false
1532
+ }
1533
+ },
1534
+ setup(__props) {
1535
+ const props = __props;
1536
+ const tag = computed(() => props.listItem ? "li" : void 0);
1537
+ const listItemClasses = computed(() => props.listItem ? "d-block" : void 0);
1538
+ return (_ctx, _cache) => {
1539
+ return openBlock(), createBlock(unref(VCol), {
1540
+ cols: "12",
1541
+ sm: props.cols,
1542
+ tag: tag.value,
1543
+ class: normalizeClass(listItemClasses.value),
1544
+ "data-testid": props.dataTestid
1545
+ }, {
1546
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
1547
+ _: 3
1548
+ }, 8, [
1549
+ "sm",
1550
+ "tag",
1551
+ "class",
1552
+ "data-testid"
1553
+ ]);
1554
+ };
1555
+ }
1556
+ });
1557
+ //#endregion
1558
+ //#region src/components/form/form.vue
1559
+ var form_default = /* @__PURE__ */ defineComponent({
1560
+ inheritAttrs: false,
1561
+ __name: "form",
1562
+ props: {
1563
+ id: {
1564
+ type: String,
1565
+ required: false
1566
+ },
1567
+ dataTestid: {
1568
+ type: String,
1569
+ required: false
1570
+ }
1571
+ },
1572
+ emits: ["submit"],
1573
+ setup(__props, { emit: __emit }) {
1574
+ const props = __props;
1575
+ const emit = __emit;
1576
+ async function onSubmit(event) {
1577
+ const { valid } = await event;
1578
+ if (valid) emit("submit", event);
1579
+ }
1580
+ return (_ctx, _cache) => {
1581
+ return openBlock(), createBlock(unref(VForm), {
1582
+ id: props.id,
1583
+ "data-testid": props.dataTestid,
1584
+ onSubmit: withModifiers(onSubmit, ["prevent"])
1585
+ }, {
1586
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
1587
+ _: 3
1588
+ }, 8, ["id", "data-testid"]);
1589
+ };
1590
+ }
1591
+ });
1592
+ //#endregion
1593
+ //#region src/components/sections/section-content/section-content.vue
1594
+ var section_content_default = /* @__PURE__ */ defineComponent({
1595
+ inheritAttrs: false,
1596
+ __name: "section-content",
1597
+ setup(__props) {
1598
+ return (_ctx, _cache) => {
1599
+ return openBlock(), createBlock(unref(VCardText), null, {
1600
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
1601
+ _: 3
1602
+ });
1603
+ };
1604
+ }
1605
+ });
1606
+ //#endregion
1607
+ //#region src/composables/section/section.ts
1608
+ const sectionVariantToVuetifyVariant = {
1609
+ primary: "elevated",
1610
+ secondary: "outlined"
1611
+ };
1612
+ function useSectionVariant(variant) {
1613
+ return computed(() => {
1614
+ const variantValue = toValue(variant);
1615
+ return variantValue ? sectionVariantToVuetifyVariant[variantValue] : sectionVariantToVuetifyVariant.primary;
1616
+ });
1617
+ }
1618
+ const sectionTextAlignToClasses = {
1619
+ start: {
1620
+ content: "text-start",
1621
+ actions: "justify-end"
1622
+ },
1623
+ center: {
1624
+ content: "text-center",
1625
+ actions: "justify-center"
1626
+ },
1627
+ end: {
1628
+ content: "text-end",
1629
+ actions: "justify-end"
1630
+ }
1631
+ };
1632
+ function useSectionTextAlign(textAlign) {
1633
+ return computed(() => {
1634
+ const textAlignValue = toValue(textAlign);
1635
+ return textAlignValue ? sectionTextAlignToClasses[textAlignValue] : sectionTextAlignToClasses.start;
1636
+ });
1637
+ }
1638
+ //#endregion
1639
+ //#region src/components/sections/section/section.vue
1640
+ var section_default = /* @__PURE__ */ defineComponent({
815
1641
  inheritAttrs: false,
816
1642
  __name: "section",
817
1643
  props: {
@@ -827,6 +1653,10 @@ var section_default = /* @__PURE__ */ defineComponent({
827
1653
  type: String,
828
1654
  required: false
829
1655
  },
1656
+ textAlign: {
1657
+ type: String,
1658
+ required: false
1659
+ },
830
1660
  actions: {
831
1661
  type: Array,
832
1662
  required: false
@@ -851,6 +1681,7 @@ var section_default = /* @__PURE__ */ defineComponent({
851
1681
  setup(__props) {
852
1682
  const props = __props;
853
1683
  const variant = useSectionVariant(() => props.variant);
1684
+ const textAlignClasses = useSectionTextAlign(() => props.textAlign);
854
1685
  const title = computed(() => props.title || void 0);
855
1686
  const subtitle = computed(() => props.subtitle || void 0);
856
1687
  const width = computed(() => props.fullWidth ? "100%" : void 0);
@@ -864,18 +1695,18 @@ var section_default = /* @__PURE__ */ defineComponent({
864
1695
  width: width.value,
865
1696
  height: height.value
866
1697
  }, {
867
- default: withCtx(() => [createVNode(unref(VCard), {
1698
+ default: withCtx(() => [createCommentVNode(" The card and its actions have no alignment props, so `textAlign` uses utility classes "), createVNode(unref(VCard), {
868
1699
  title: title.value,
869
1700
  subtitle: subtitle.value,
870
1701
  variant: unref(variant),
871
1702
  width: width.value,
872
1703
  height: height.value,
873
1704
  "data-testid": props.dataTestid,
874
- class: "d-flex flex-column"
1705
+ class: normalizeClass(["d-flex flex-column", unref(textAlignClasses).content])
875
1706
  }, {
876
1707
  default: withCtx(() => [renderSlot(_ctx.$slots, "default"), showActions.value ? (openBlock(), createBlock(unref(VCardActions), {
877
1708
  key: 0,
878
- class: "mt-auto"
1709
+ class: normalizeClass(["mt-auto", unref(textAlignClasses).actions])
879
1710
  }, {
880
1711
  default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(props.actions, ({ label, onClick, ...action }, index) => {
881
1712
  return openBlock(), createBlock(button_default, mergeProps({ key: index }, { ref_for: true }, action, { onClick }), {
@@ -884,7 +1715,7 @@ var section_default = /* @__PURE__ */ defineComponent({
884
1715
  }, 1040, ["onClick"]);
885
1716
  }), 128))]),
886
1717
  _: 1
887
- })) : createCommentVNode("v-if", true)]),
1718
+ }, 8, ["class"])) : createCommentVNode("v-if", true)]),
888
1719
  _: 3
889
1720
  }, 8, [
890
1721
  "title",
@@ -892,7 +1723,8 @@ var section_default = /* @__PURE__ */ defineComponent({
892
1723
  "variant",
893
1724
  "width",
894
1725
  "height",
895
- "data-testid"
1726
+ "data-testid",
1727
+ "class"
896
1728
  ])]),
897
1729
  _: 3
898
1730
  }, 8, [
@@ -987,4 +1819,354 @@ var table_default = /* @__PURE__ */ defineComponent({
987
1819
  }
988
1820
  });
989
1821
  //#endregion
990
- export { button_default as UButton, column_default as UColumn, container_default as UContainer, form_default as UForm, main_default as UMain, row_default as URow, section_default as USection, section_content_default as USectionContent, table_default as UTable, text_field_default as UTextField };
1822
+ //#region src/components/details/details.vue
1823
+ var details_default = /* @__PURE__ */ defineComponent({
1824
+ inheritAttrs: false,
1825
+ __name: "details",
1826
+ props: {
1827
+ variant: {
1828
+ type: String,
1829
+ required: false
1830
+ },
1831
+ title: {
1832
+ type: String,
1833
+ required: false
1834
+ },
1835
+ loading: {
1836
+ type: Boolean,
1837
+ required: false
1838
+ },
1839
+ dataTestid: {
1840
+ type: String,
1841
+ required: false
1842
+ }
1843
+ },
1844
+ setup(__props) {
1845
+ const props = __props;
1846
+ const outlined = computed(() => props.variant === "secondary");
1847
+ return (_ctx, _cache) => {
1848
+ return openBlock(), createBlock(unref(VSkeletonLoader), {
1849
+ loading: props.loading,
1850
+ type: "heading",
1851
+ width: "100%"
1852
+ }, {
1853
+ default: withCtx(() => [createVNode(unref(VExpansionPanels), {
1854
+ flat: outlined.value,
1855
+ "data-testid": props.dataTestid
1856
+ }, {
1857
+ default: withCtx(() => [createVNode(unref(VExpansionPanel), { class: normalizeClass({ border: outlined.value }) }, {
1858
+ default: withCtx(() => [createVNode(unref(VExpansionPanelTitle), null, {
1859
+ default: withCtx(() => [createTextVNode(toDisplayString(props.title), 1)]),
1860
+ _: 1
1861
+ }), createVNode(unref(VExpansionPanelText), null, {
1862
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
1863
+ _: 3
1864
+ })]),
1865
+ _: 3
1866
+ }, 8, ["class"])]),
1867
+ _: 3
1868
+ }, 8, ["flat", "data-testid"])]),
1869
+ _: 3
1870
+ }, 8, ["loading"]);
1871
+ };
1872
+ }
1873
+ });
1874
+ //#endregion
1875
+ //#region src/composables/data-set/data-set.ts
1876
+ /**
1877
+ * Width, out of the grid's 12 columns, taken by each record so that
1878
+ * `columns` records fit side by side on a row.
1879
+ */
1880
+ const dataSetColumnsToColumnCols = {
1881
+ 1: "12",
1882
+ 2: "6",
1883
+ 3: "4",
1884
+ 4: "3",
1885
+ 6: "2"
1886
+ };
1887
+ /**
1888
+ * Space, in pixels, between two cards of the same row: the default gutter of
1889
+ * `URow`/`UColumn` (Vuetify's `v-row` with its default density puts a 24px
1890
+ * gap between its columns). `n` cards side by side take `n` card widths plus
1891
+ * `n - 1` gaps, so they fit in `width` pixels when
1892
+ * `n * (minDataSetItemWidth + gap) <= width + gap`.
1893
+ */
1894
+ const dataSetItemGap = 24;
1895
+ /**
1896
+ * Supported numbers of records per row, from the largest to the smallest.
1897
+ */
1898
+ const dataSetColumnsOptions = [
1899
+ 6,
1900
+ 4,
1901
+ 3,
1902
+ 2,
1903
+ 1
1904
+ ];
1905
+ /**
1906
+ * Number of records to place side by side: the largest supported amount, up
1907
+ * to `columns`, that keeps each card at least {@link minDataSetItemWidth}
1908
+ * pixels wide, besides the {@link dataSetItemGap} between cards, in a data set
1909
+ * `width` pixels wide, but never less than one.
1910
+ * While the width is unknown (`0`, e.g. before the first measure or during
1911
+ * server-side rendering), `columns` is used as is.
1912
+ */
1913
+ function getDataSetFittingColumns(columns, width) {
1914
+ if (!width) return columns;
1915
+ const fittingColumns = Math.min(columns, Math.floor((width + dataSetItemGap) / 264));
1916
+ return dataSetColumnsOptions.find((option) => option <= fittingColumns) ?? 1;
1917
+ }
1918
+ /**
1919
+ * Reactive version of {@link getDataSetFittingColumns}.
1920
+ */
1921
+ function useDataSetFittingColumns(columns, width) {
1922
+ return computed(() => getDataSetFittingColumns(toValue(columns), toValue(width)));
1923
+ }
1924
+ /**
1925
+ * Width, in pixels, of the given element, kept up to date with a
1926
+ * `ResizeObserver`. It's `0` while there's no element, e.g. before it's
1927
+ * mounted, and the observer is disconnected when the element changes or the
1928
+ * calling scope is disposed.
1929
+ */
1930
+ function useDataSetWidth(element) {
1931
+ const width = ref(0);
1932
+ let observer;
1933
+ function disconnect() {
1934
+ observer?.disconnect();
1935
+ observer = void 0;
1936
+ }
1937
+ watch(() => toValue(element), (currentElement) => {
1938
+ disconnect();
1939
+ width.value = 0;
1940
+ if (!currentElement) return;
1941
+ observer = new ResizeObserver(([entry]) => {
1942
+ width.value = entry.contentRect.width;
1943
+ });
1944
+ observer.observe(currentElement);
1945
+ }, { immediate: true });
1946
+ onScopeDispose(disconnect);
1947
+ return width;
1948
+ }
1949
+ /**
1950
+ * Maps the number of records per row to the width of each record's grid
1951
+ * column.
1952
+ */
1953
+ function useDataSetColumnCols(columns) {
1954
+ return computed(() => dataSetColumnsToColumnCols[toValue(columns)]);
1955
+ }
1956
+ /**
1957
+ * Maps the record properties considered by the search to Vuetify's filter
1958
+ * keys. An empty list means every property, just like an undefined one.
1959
+ */
1960
+ function useDataSetFilterKeys(searchKeys) {
1961
+ return computed(() => {
1962
+ const keys = toValue(searchKeys);
1963
+ return keys?.length ? keys : void 0;
1964
+ });
1965
+ }
1966
+ /**
1967
+ * Up to this many pages, every page button is shown. Vuetify's automatic
1968
+ * sizing collapses 3 pages into `1 … 2 … 3` in containers narrower than
1969
+ * ~336px, although showing every page never takes more room than its
1970
+ * collapsed form (`1 … x … 5`, 5 buttons) up to this amount.
1971
+ */
1972
+ const maxAlwaysVisiblePages = 5;
1973
+ /**
1974
+ * Number of page buttons to show for `pageCount` pages: every page up to
1975
+ * {@link maxAlwaysVisiblePages}, otherwise `undefined` to let Vuetify fit them
1976
+ * to the available width.
1977
+ */
1978
+ function getDataSetTotalVisible(pageCount) {
1979
+ return pageCount <= maxAlwaysVisiblePages ? pageCount : void 0;
1980
+ }
1981
+ /**
1982
+ * Goes back to the first page whenever the search changes. It must run before
1983
+ * the new search reaches Vuetify's iterator, which would otherwise clamp the
1984
+ * outdated page to the last one of the filtered records.
1985
+ */
1986
+ function useDataSetSearchPageReset(search, page) {
1987
+ watch(() => toValue(search), () => {
1988
+ page.value = 1;
1989
+ });
1990
+ }
1991
+ //#endregion
1992
+ //#region src/components/data-sets/data-set/data-set.vue
1993
+ var data_set_default = /* @__PURE__ */ defineComponent({
1994
+ inheritAttrs: false,
1995
+ __name: "data-set",
1996
+ props: /*@__PURE__*/ mergeModels({
1997
+ items: {
1998
+ type: Array,
1999
+ required: false
2000
+ },
2001
+ columns: {
2002
+ type: Number,
2003
+ required: false,
2004
+ default: 3
2005
+ },
2006
+ itemsPerPage: {
2007
+ type: Number,
2008
+ required: false,
2009
+ default: 10
2010
+ },
2011
+ searchable: {
2012
+ type: Boolean,
2013
+ required: false
2014
+ },
2015
+ searchKeys: {
2016
+ type: Array,
2017
+ required: false
2018
+ },
2019
+ noDataText: {
2020
+ type: String,
2021
+ required: false,
2022
+ default: "Nenhum registro encontrado."
2023
+ },
2024
+ loading: {
2025
+ type: Boolean,
2026
+ required: false
2027
+ },
2028
+ dataTestid: {
2029
+ type: String,
2030
+ required: false
2031
+ }
2032
+ }, {
2033
+ "page": {
2034
+ type: Number,
2035
+ default: 1
2036
+ },
2037
+ "pageModifiers": {},
2038
+ "search": {
2039
+ type: String,
2040
+ default: ""
2041
+ },
2042
+ "searchModifiers": {}
2043
+ }),
2044
+ emits: ["update:page", "update:search"],
2045
+ setup(__props) {
2046
+ const props = __props;
2047
+ useSlots();
2048
+ const page = useModel(__props, "page");
2049
+ const search = useModel(__props, "search");
2050
+ const iterator = useTemplateRef("iterator");
2051
+ const columnCols = useDataSetColumnCols(useDataSetFittingColumns(() => props.columns, useDataSetWidth(() => iterator.value?.$el)));
2052
+ const filterKeys = useDataSetFilterKeys(() => props.searchKeys);
2053
+ useDataSetSearchPageReset(search, page);
2054
+ return (_ctx, _cache) => {
2055
+ return openBlock(), createBlock(unref(VSkeletonLoader), {
2056
+ loading: props.loading,
2057
+ type: "card",
2058
+ width: "100%"
2059
+ }, {
2060
+ default: withCtx(() => [createVNode(unref(VDataIterator), {
2061
+ ref_key: "iterator",
2062
+ ref: iterator,
2063
+ page: page.value,
2064
+ "onUpdate:page": _cache[2] || (_cache[2] = ($event) => page.value = $event),
2065
+ items: props.items,
2066
+ "items-per-page": props.itemsPerPage,
2067
+ search: search.value,
2068
+ "filter-keys": unref(filterKeys),
2069
+ "data-testid": props.dataTestid
2070
+ }, {
2071
+ header: withCtx(() => [props.searchable ? (openBlock(), createBlock(text_field_default, {
2072
+ key: 0,
2073
+ modelValue: search.value,
2074
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
2075
+ type: "search",
2076
+ label: "Pesquisar"
2077
+ }, null, 8, ["modelValue"])) : createCommentVNode("v-if", true)]),
2078
+ default: withCtx(({ items: pageItems }) => [createCommentVNode(" Stretched, so the items of the same line (e.g. full-height cards) share its height "), createVNode(row_default, {
2079
+ list: "",
2080
+ align: "stretch"
2081
+ }, {
2082
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(pageItems, (pageItem, index) => {
2083
+ return openBlock(), createBlock(column_default, {
2084
+ key: index,
2085
+ cols: unref(columnCols),
2086
+ "list-item": ""
2087
+ }, {
2088
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default", {
2089
+ item: pageItem.raw,
2090
+ index
2091
+ })]),
2092
+ _: 2
2093
+ }, 1032, ["cols"]);
2094
+ }), 128))]),
2095
+ _: 2
2096
+ }, 1024)]),
2097
+ "no-data": withCtx(() => [createVNode(unref(VEmptyState), { text: props.noDataText }, null, 8, ["text"])]),
2098
+ footer: withCtx(({ pageCount }) => [pageCount > 1 ? (openBlock(), createBlock(unref(VPagination), {
2099
+ key: 0,
2100
+ modelValue: page.value,
2101
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => page.value = $event),
2102
+ length: pageCount,
2103
+ "total-visible": unref(getDataSetTotalVisible)(pageCount)
2104
+ }, null, 8, [
2105
+ "modelValue",
2106
+ "length",
2107
+ "total-visible"
2108
+ ])) : createCommentVNode("v-if", true)]),
2109
+ _: 3
2110
+ }, 8, [
2111
+ "page",
2112
+ "items",
2113
+ "items-per-page",
2114
+ "search",
2115
+ "filter-keys",
2116
+ "data-testid"
2117
+ ])]),
2118
+ _: 3
2119
+ }, 8, ["loading"]);
2120
+ };
2121
+ }
2122
+ });
2123
+ //#endregion
2124
+ //#region src/components/data-sets/data-set-item/data-set-item.vue
2125
+ var data_set_item_default = /* @__PURE__ */ defineComponent({
2126
+ inheritAttrs: false,
2127
+ __name: "data-set-item",
2128
+ props: {
2129
+ title: {
2130
+ type: String,
2131
+ required: false
2132
+ },
2133
+ subtitle: {
2134
+ type: String,
2135
+ required: false
2136
+ },
2137
+ variant: {
2138
+ type: String,
2139
+ required: false
2140
+ },
2141
+ dataTestid: {
2142
+ type: String,
2143
+ required: false
2144
+ }
2145
+ },
2146
+ setup(__props) {
2147
+ const props = __props;
2148
+ const slots = useSlots();
2149
+ return (_ctx, _cache) => {
2150
+ return openBlock(), createBlock(section_default, {
2151
+ title: props.title,
2152
+ subtitle: props.subtitle,
2153
+ variant: props.variant,
2154
+ "data-testid": props.dataTestid,
2155
+ "full-height": ""
2156
+ }, {
2157
+ default: withCtx(() => [slots.default ? (openBlock(), createBlock(section_content_default, { key: 0 }, {
2158
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
2159
+ _: 3
2160
+ })) : createCommentVNode("v-if", true)]),
2161
+ _: 3
2162
+ }, 8, [
2163
+ "title",
2164
+ "subtitle",
2165
+ "variant",
2166
+ "data-testid"
2167
+ ]);
2168
+ };
2169
+ }
2170
+ });
2171
+ //#endregion
2172
+ 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, main_default as UMain, row_default as URow, section_default as USection, section_content_default as USectionContent, table_default as UTable, text_field_default as UTextField };