ads-nuxt 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +567 -1
  3. package/app/app.config.ts +25 -25
  4. package/app/assets/ads/checkbox-check.svg +5 -5
  5. package/app/assets/ads/checkbox-minus.svg +5 -5
  6. package/app/assets/brand/farmarcas.svg +6 -6
  7. package/app/assets/brand/radar.svg +9 -9
  8. package/app/assets/css/component-tokens.css +403 -403
  9. package/app/assets/css/main.css +617 -617
  10. package/app/components/AdsBadge.vue +48 -48
  11. package/app/components/AdsBroadcast.vue +45 -45
  12. package/app/components/AdsButton.vue +569 -569
  13. package/app/components/AdsButtonGroup.vue +39 -39
  14. package/app/components/AdsButtonMenu.vue +48 -48
  15. package/app/components/AdsCard.vue +117 -117
  16. package/app/components/AdsCharttip.vue +78 -78
  17. package/app/components/AdsCheckbox.vue +77 -77
  18. package/app/components/AdsDatePicker.vue +473 -473
  19. package/app/components/AdsDropdownMenu.vue +19 -19
  20. package/app/components/AdsEmptyState.vue +58 -58
  21. package/app/components/AdsFarmarcasPill.vue +74 -74
  22. package/app/components/AdsFeatureCard.vue +217 -217
  23. package/app/components/AdsFileCard.vue +101 -101
  24. package/app/components/AdsFilterDropdown.vue +174 -174
  25. package/app/components/AdsFilterOption.vue +72 -72
  26. package/app/components/AdsFilterSelect.vue +202 -202
  27. package/app/components/AdsFooter.vue +25 -25
  28. package/app/components/AdsInput.vue +143 -143
  29. package/app/components/AdsListDropdown.vue +44 -44
  30. package/app/components/AdsLoading.vue +109 -109
  31. package/app/components/AdsMenuBar.vue +103 -103
  32. package/app/components/AdsMetricCard.vue +119 -119
  33. package/app/components/AdsMiniPill.vue +40 -40
  34. package/app/components/AdsModal.vue +157 -157
  35. package/app/components/AdsMonthPicker.vue +80 -80
  36. package/app/components/AdsNavbarApp.vue +67 -67
  37. package/app/components/AdsNavbarDesk.vue +62 -62
  38. package/app/components/AdsNotabilityPill.vue +27 -27
  39. package/app/components/AdsNumberInput.vue +166 -166
  40. package/app/components/AdsPagination.vue +91 -91
  41. package/app/components/AdsProductTag.vue +61 -61
  42. package/app/components/AdsRadio.vue +70 -70
  43. package/app/components/AdsSearchInput.vue +113 -113
  44. package/app/components/AdsSegmentedTabs.vue +28 -28
  45. package/app/components/AdsSideMenu.vue +101 -101
  46. package/app/components/AdsSmalltip.vue +44 -44
  47. package/app/components/AdsSteptip.vue +101 -101
  48. package/app/components/AdsSwitch.vue +59 -59
  49. package/app/components/AdsTable.vue +100 -100
  50. package/app/components/AdsTableToolbar.vue +44 -44
  51. package/app/components/AdsTabs.vue +51 -51
  52. package/app/components/AdsTextarea.vue +109 -109
  53. package/app/components/AdsToast.vue +181 -181
  54. package/app/components/AdsTooltip.vue +66 -66
  55. package/app/components/InvertedTooltip.vue +40 -40
  56. package/app/components/OverlayLoading.vue +173 -173
  57. package/app/components/ProgressBarLoading.vue +256 -256
  58. package/app/composables/useOverlayLoading.ts +66 -66
  59. package/app/locale/pt-br.ts +6 -6
  60. package/app/plugins/ads-toast.client.ts +24 -24
  61. package/app/theme/ads-theme.ts +370 -370
  62. package/app/theme/component-tokens.json +402 -402
  63. package/app/theme/component-tokens.ts +815 -815
  64. package/app/types/overlay-loading.types.ts +26 -26
  65. package/app/utils/ads-filter-select.ts +172 -172
  66. package/app/utils/ads-table.ts +204 -204
  67. package/app/utils/calendar-date.ts +128 -128
  68. package/app/utils/category-role.ts +45 -45
  69. package/app/utils/money.ts +77 -77
  70. package/app/utils/notability.ts +8 -8
  71. package/app/utils/parser.ts +134 -134
  72. package/app/utils/percentage.ts +54 -54
  73. package/nuxt.config.ts +31 -32
  74. package/package.json +48 -46
@@ -1,256 +1,256 @@
1
- <template>
2
- <div :class="rootClass">
3
- <div v-if="hasHeader" class="flex items-start gap-3">
4
- <span
5
- v-if="showIcon"
6
- class="inline-flex size-10 shrink-0 items-center justify-center rounded-lg border border-primary-200 bg-primary-50"
7
- >
8
- <UIcon
9
- :name="icon"
10
- class="size-6 text-primary"
11
- :class="{ 'animate-spin': spinIcon }"
12
- aria-hidden="true"
13
- />
14
- </span>
15
- <div class="min-w-0 flex-1">
16
- <p v-if="title" class="font-semibold text-highlighted">{{ title }}</p>
17
- <p
18
- v-if="description"
19
- class="text-sm text-muted"
20
- :class="title ? 'mt-0.5' : ''"
21
- >
22
- {{ description }}
23
- </p>
24
- </div>
25
- <span
26
- v-if="reservePercentSlot"
27
- class="shrink-0 text-sm font-medium tabular-nums text-highlighted"
28
- :class="{ invisible: !isPercentVisible }"
29
- :aria-hidden="!isPercentVisible"
30
- >
31
- {{ percentClamped }}%
32
- </span>
33
- </div>
34
-
35
- <div
36
- class="h-[var(--ads-progress-height)] w-full overflow-hidden rounded-[var(--ads-progress-radius)] bg-[var(--ads-progress-track)]"
37
- role="progressbar"
38
- :aria-valuenow="indeterminate ? undefined : percentClamped"
39
- aria-valuemin="0"
40
- aria-valuemax="100"
41
- :aria-busy="indeterminate ? true : undefined"
42
- :aria-label="effectiveAriaLabel"
43
- >
44
- <div
45
- v-if="indeterminate"
46
- class="progress-bar-loading__indeterminate h-full rounded-full"
47
- :class="fillClass"
48
- />
49
- <div
50
- v-else
51
- class="h-full rounded-full transition-[width] duration-300 ease-out"
52
- :class="fillClass"
53
- :style="{ width: `${percentClamped}%` }"
54
- />
55
- </div>
56
-
57
- <div
58
- v-if="reserveElapsedTimeSlot"
59
- class="flex justify-end"
60
- >
61
- <span
62
- class="inline-flex items-center gap-1 font-mono text-xs tabular-nums text-muted"
63
- :class="{ invisible: !showElapsedTime }"
64
- role="timer"
65
- aria-live="off"
66
- :aria-hidden="!showElapsedTime"
67
- :aria-label="`Tempo decorrido: ${elapsedDisplay}`"
68
- >
69
- <UIcon
70
- name="i-lucide-timer"
71
- class="size-3.5 text-muted"
72
- aria-hidden="true"
73
- />
74
- {{ elapsedDisplay }}
75
- </span>
76
- </div>
77
- </div>
78
- </template>
79
-
80
- <script setup lang="ts">
81
- import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
82
-
83
- type ProgressBarLoadingVariant = "primary" | "success" | "error";
84
-
85
- interface Props {
86
- title?: string;
87
- description?: string;
88
- icon?: string;
89
- showIcon?: boolean;
90
- spinIcon?: boolean;
91
- indeterminate?: boolean;
92
- percent?: number;
93
- showPercent?: boolean;
94
- showElapsedTime?: boolean;
95
- ariaLabel?: string;
96
- variant?: ProgressBarLoadingVariant;
97
- rootClass?: string;
98
- }
99
-
100
- const props = withDefaults(defineProps<Props>(), {
101
- title: "",
102
- description: "",
103
- icon: "i-lucide-loader-circle",
104
- showIcon: true,
105
- spinIcon: true,
106
- indeterminate: false,
107
- percent: 0,
108
- showPercent: true,
109
- showElapsedTime: true,
110
- ariaLabel: "",
111
- variant: "primary",
112
- rootClass: "flex flex-col gap-3",
113
- });
114
-
115
- const percentClamped = computed(() =>
116
- Math.max(0, Math.min(100, Math.round(props.percent ?? 0))),
117
- );
118
-
119
- const fillClass = computed(() => {
120
- if (props.variant === "success") return "bg-success-500";
121
- if (props.variant === "error") return "bg-error-500";
122
- return "bg-[var(--ads-progress-fill)]";
123
- });
124
-
125
- const effectiveAriaLabel = computed(
126
- () => props.ariaLabel || props.title || "Carregando",
127
- );
128
-
129
- const isPercentVisible = computed(
130
- () => Boolean(props.showPercent) && !props.indeterminate,
131
- );
132
-
133
- /**
134
- * Slots reservados de forma "sticky-true": uma vez que o slot é necessário (header,
135
- * timer ou percent), mantemos o nó renderizado para sempre. Isso evita que mudanças
136
- * de status (ex.: pipeline `uploading` → `success`, `indeterminate` → determinado,
137
- * `showElapsedTime` true → false) movam outros elementos no eixo Y.
138
- *
139
- * A alternância visual é feita apenas via `invisible` (mantém o espaço reservado).
140
- */
141
- const computedHasHeader = () =>
142
- Boolean(props.showIcon) ||
143
- Boolean(props.title) ||
144
- Boolean(props.description) ||
145
- (!props.indeterminate && Boolean(props.showPercent));
146
-
147
- const hasHeader = ref(computedHasHeader());
148
- const reserveElapsedTimeSlot = ref(Boolean(props.showElapsedTime));
149
- const reservePercentSlot = ref(Boolean(props.showPercent));
150
-
151
- watch(computedHasHeader, (val) => {
152
- if (val) hasHeader.value = true;
153
- });
154
-
155
- watch(
156
- () => Boolean(props.showElapsedTime),
157
- (val) => {
158
- if (val) reserveElapsedTimeSlot.value = true;
159
- },
160
- );
161
-
162
- watch(
163
- () => Boolean(props.showPercent),
164
- (val) => {
165
- if (val) reservePercentSlot.value = true;
166
- },
167
- );
168
-
169
- const startedAt = ref<number>(0);
170
- const now = ref<number>(0);
171
- let elapsedTimerId: ReturnType<typeof setInterval> | null = null;
172
-
173
- const stopElapsedTimer = () => {
174
- if (elapsedTimerId != null) {
175
- clearInterval(elapsedTimerId);
176
- elapsedTimerId = null;
177
- }
178
- };
179
-
180
- const startElapsedTimer = () => {
181
- stopElapsedTimer();
182
- startedAt.value = Date.now();
183
- now.value = startedAt.value;
184
- if (typeof window === "undefined") {
185
- return;
186
- }
187
- elapsedTimerId = setInterval(() => {
188
- now.value = Date.now();
189
- }, 1000);
190
- };
191
-
192
- const elapsedSeconds = computed(() =>
193
- Math.max(0, Math.floor((now.value - startedAt.value) / 1000)),
194
- );
195
-
196
- const elapsedDisplay = computed(() => {
197
- const total = elapsedSeconds.value;
198
- const hours = Math.floor(total / 3600);
199
- const minutes = Math.floor((total % 3600) / 60);
200
- const seconds = total % 60;
201
- const pad = (n: number) => String(n).padStart(2, "0");
202
- if (hours > 0) {
203
- return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
204
- }
205
- return `${pad(minutes)}:${pad(seconds)}`;
206
- });
207
-
208
- watch(
209
- () => props.showElapsedTime,
210
- (enabled) => {
211
- if (enabled) {
212
- startElapsedTimer();
213
- } else {
214
- stopElapsedTimer();
215
- }
216
- },
217
- );
218
-
219
- onMounted(() => {
220
- if (props.showElapsedTime) {
221
- startElapsedTimer();
222
- }
223
- });
224
-
225
- onBeforeUnmount(() => {
226
- stopElapsedTimer();
227
- });
228
- </script>
229
-
230
- <style scoped>
231
- .progress-bar-loading__indeterminate {
232
- width: 35%;
233
- animation: progress-bar-loading-indeterminate 1.4s ease-in-out infinite;
234
- transform-origin: left;
235
- }
236
-
237
- @keyframes progress-bar-loading-indeterminate {
238
- 0% {
239
- margin-inline-start: -35%;
240
- }
241
- 60% {
242
- margin-inline-start: 100%;
243
- }
244
- 100% {
245
- margin-inline-start: 100%;
246
- }
247
- }
248
-
249
- @media (prefers-reduced-motion: reduce) {
250
- .progress-bar-loading__indeterminate {
251
- width: 100%;
252
- animation: none;
253
- opacity: 0.6;
254
- }
255
- }
256
- </style>
1
+ <template>
2
+ <div :class="rootClass">
3
+ <div v-if="hasHeader" class="flex items-start gap-3">
4
+ <span
5
+ v-if="showIcon"
6
+ class="inline-flex size-10 shrink-0 items-center justify-center rounded-lg border border-primary-200 bg-primary-50"
7
+ >
8
+ <UIcon
9
+ :name="icon"
10
+ class="size-6 text-primary"
11
+ :class="{ 'animate-spin': spinIcon }"
12
+ aria-hidden="true"
13
+ />
14
+ </span>
15
+ <div class="min-w-0 flex-1">
16
+ <p v-if="title" class="font-semibold text-highlighted">{{ title }}</p>
17
+ <p
18
+ v-if="description"
19
+ class="text-sm text-muted"
20
+ :class="title ? 'mt-0.5' : ''"
21
+ >
22
+ {{ description }}
23
+ </p>
24
+ </div>
25
+ <span
26
+ v-if="reservePercentSlot"
27
+ class="shrink-0 text-sm font-medium tabular-nums text-highlighted"
28
+ :class="{ invisible: !isPercentVisible }"
29
+ :aria-hidden="!isPercentVisible"
30
+ >
31
+ {{ percentClamped }}%
32
+ </span>
33
+ </div>
34
+
35
+ <div
36
+ class="h-[var(--ads-progress-height)] w-full overflow-hidden rounded-[var(--ads-progress-radius)] bg-[var(--ads-progress-track)]"
37
+ role="progressbar"
38
+ :aria-valuenow="indeterminate ? undefined : percentClamped"
39
+ aria-valuemin="0"
40
+ aria-valuemax="100"
41
+ :aria-busy="indeterminate ? true : undefined"
42
+ :aria-label="effectiveAriaLabel"
43
+ >
44
+ <div
45
+ v-if="indeterminate"
46
+ class="progress-bar-loading__indeterminate h-full rounded-full"
47
+ :class="fillClass"
48
+ />
49
+ <div
50
+ v-else
51
+ class="h-full rounded-full transition-[width] duration-300 ease-out"
52
+ :class="fillClass"
53
+ :style="{ width: `${percentClamped}%` }"
54
+ />
55
+ </div>
56
+
57
+ <div
58
+ v-if="reserveElapsedTimeSlot"
59
+ class="flex justify-end"
60
+ >
61
+ <span
62
+ class="inline-flex items-center gap-1 font-mono text-xs tabular-nums text-muted"
63
+ :class="{ invisible: !showElapsedTime }"
64
+ role="timer"
65
+ aria-live="off"
66
+ :aria-hidden="!showElapsedTime"
67
+ :aria-label="`Tempo decorrido: ${elapsedDisplay}`"
68
+ >
69
+ <UIcon
70
+ name="i-lucide-timer"
71
+ class="size-3.5 text-muted"
72
+ aria-hidden="true"
73
+ />
74
+ {{ elapsedDisplay }}
75
+ </span>
76
+ </div>
77
+ </div>
78
+ </template>
79
+
80
+ <script setup lang="ts">
81
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
82
+
83
+ type ProgressBarLoadingVariant = "primary" | "success" | "error";
84
+
85
+ interface Props {
86
+ title?: string;
87
+ description?: string;
88
+ icon?: string;
89
+ showIcon?: boolean;
90
+ spinIcon?: boolean;
91
+ indeterminate?: boolean;
92
+ percent?: number;
93
+ showPercent?: boolean;
94
+ showElapsedTime?: boolean;
95
+ ariaLabel?: string;
96
+ variant?: ProgressBarLoadingVariant;
97
+ rootClass?: string;
98
+ }
99
+
100
+ const props = withDefaults(defineProps<Props>(), {
101
+ title: "",
102
+ description: "",
103
+ icon: "i-lucide-loader-circle",
104
+ showIcon: true,
105
+ spinIcon: true,
106
+ indeterminate: false,
107
+ percent: 0,
108
+ showPercent: true,
109
+ showElapsedTime: true,
110
+ ariaLabel: "",
111
+ variant: "primary",
112
+ rootClass: "flex flex-col gap-3",
113
+ });
114
+
115
+ const percentClamped = computed(() =>
116
+ Math.max(0, Math.min(100, Math.round(props.percent ?? 0))),
117
+ );
118
+
119
+ const fillClass = computed(() => {
120
+ if (props.variant === "success") return "bg-success-500";
121
+ if (props.variant === "error") return "bg-error-500";
122
+ return "bg-[var(--ads-progress-fill)]";
123
+ });
124
+
125
+ const effectiveAriaLabel = computed(
126
+ () => props.ariaLabel || props.title || "Carregando",
127
+ );
128
+
129
+ const isPercentVisible = computed(
130
+ () => Boolean(props.showPercent) && !props.indeterminate,
131
+ );
132
+
133
+ /**
134
+ * Slots reservados de forma "sticky-true": uma vez que o slot é necessário (header,
135
+ * timer ou percent), mantemos o nó renderizado para sempre. Isso evita que mudanças
136
+ * de status (ex.: pipeline `uploading` → `success`, `indeterminate` → determinado,
137
+ * `showElapsedTime` true → false) movam outros elementos no eixo Y.
138
+ *
139
+ * A alternância visual é feita apenas via `invisible` (mantém o espaço reservado).
140
+ */
141
+ const computedHasHeader = () =>
142
+ Boolean(props.showIcon) ||
143
+ Boolean(props.title) ||
144
+ Boolean(props.description) ||
145
+ (!props.indeterminate && Boolean(props.showPercent));
146
+
147
+ const hasHeader = ref(computedHasHeader());
148
+ const reserveElapsedTimeSlot = ref(Boolean(props.showElapsedTime));
149
+ const reservePercentSlot = ref(Boolean(props.showPercent));
150
+
151
+ watch(computedHasHeader, (val) => {
152
+ if (val) hasHeader.value = true;
153
+ });
154
+
155
+ watch(
156
+ () => Boolean(props.showElapsedTime),
157
+ (val) => {
158
+ if (val) reserveElapsedTimeSlot.value = true;
159
+ },
160
+ );
161
+
162
+ watch(
163
+ () => Boolean(props.showPercent),
164
+ (val) => {
165
+ if (val) reservePercentSlot.value = true;
166
+ },
167
+ );
168
+
169
+ const startedAt = ref<number>(0);
170
+ const now = ref<number>(0);
171
+ let elapsedTimerId: ReturnType<typeof setInterval> | null = null;
172
+
173
+ const stopElapsedTimer = () => {
174
+ if (elapsedTimerId != null) {
175
+ clearInterval(elapsedTimerId);
176
+ elapsedTimerId = null;
177
+ }
178
+ };
179
+
180
+ const startElapsedTimer = () => {
181
+ stopElapsedTimer();
182
+ startedAt.value = Date.now();
183
+ now.value = startedAt.value;
184
+ if (typeof window === "undefined") {
185
+ return;
186
+ }
187
+ elapsedTimerId = setInterval(() => {
188
+ now.value = Date.now();
189
+ }, 1000);
190
+ };
191
+
192
+ const elapsedSeconds = computed(() =>
193
+ Math.max(0, Math.floor((now.value - startedAt.value) / 1000)),
194
+ );
195
+
196
+ const elapsedDisplay = computed(() => {
197
+ const total = elapsedSeconds.value;
198
+ const hours = Math.floor(total / 3600);
199
+ const minutes = Math.floor((total % 3600) / 60);
200
+ const seconds = total % 60;
201
+ const pad = (n: number) => String(n).padStart(2, "0");
202
+ if (hours > 0) {
203
+ return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
204
+ }
205
+ return `${pad(minutes)}:${pad(seconds)}`;
206
+ });
207
+
208
+ watch(
209
+ () => props.showElapsedTime,
210
+ (enabled) => {
211
+ if (enabled) {
212
+ startElapsedTimer();
213
+ } else {
214
+ stopElapsedTimer();
215
+ }
216
+ },
217
+ );
218
+
219
+ onMounted(() => {
220
+ if (props.showElapsedTime) {
221
+ startElapsedTimer();
222
+ }
223
+ });
224
+
225
+ onBeforeUnmount(() => {
226
+ stopElapsedTimer();
227
+ });
228
+ </script>
229
+
230
+ <style scoped>
231
+ .progress-bar-loading__indeterminate {
232
+ width: 35%;
233
+ animation: progress-bar-loading-indeterminate 1.4s ease-in-out infinite;
234
+ transform-origin: left;
235
+ }
236
+
237
+ @keyframes progress-bar-loading-indeterminate {
238
+ 0% {
239
+ margin-inline-start: -35%;
240
+ }
241
+ 60% {
242
+ margin-inline-start: 100%;
243
+ }
244
+ 100% {
245
+ margin-inline-start: 100%;
246
+ }
247
+ }
248
+
249
+ @media (prefers-reduced-motion: reduce) {
250
+ .progress-bar-loading__indeterminate {
251
+ width: 100%;
252
+ animation: none;
253
+ opacity: 0.6;
254
+ }
255
+ }
256
+ </style>
@@ -1,66 +1,66 @@
1
- import type { OverlayLoadingOptions } from "../types/overlay-loading.types";
2
-
3
- export const useOverlayLoading = () => {
4
- const overlay = useOverlay();
5
- const OverlayLoadingComponent = defineAsyncComponent(
6
- () => import("../components/OverlayLoading.vue")
7
- );
8
-
9
- let currentOverlay: {
10
- id: symbol;
11
- open: () => void;
12
- close: () => void;
13
- patch: (props: Record<string, unknown>) => void;
14
- } | null = null;
15
- const show = (options: OverlayLoadingOptions) => {
16
- if (currentOverlay) {
17
- currentOverlay.close();
18
- }
19
- currentOverlay = overlay.create(OverlayLoadingComponent, {
20
- destroyOnClose: true,
21
- props: {
22
- isVisible: true,
23
- ...options,
24
- },
25
- });
26
-
27
- currentOverlay?.open();
28
-
29
- return currentOverlay?.id ?? null;
30
- };
31
-
32
- const close = () => {
33
- if (currentOverlay) {
34
- currentOverlay.close();
35
- currentOverlay = null;
36
- }
37
- };
38
-
39
- /**
40
- * Mescla props no componente do overlay. Props devem ser planas (title, description, …):
41
- * o `patch` do useOverlay faz `{ ...overlay.props, ...patch }`, não aceita `{ props: { … } }`.
42
- */
43
- const update = (options: Partial<OverlayLoadingOptions>) => {
44
- if (currentOverlay) {
45
- currentOverlay.patch({
46
- isVisible: true,
47
- ...options,
48
- });
49
- }
50
- };
51
-
52
- const isOpen = () => {
53
- return currentOverlay !== null;
54
- };
55
-
56
- const getCurrentId = () => currentOverlay?.id ?? null;
57
-
58
- return {
59
- show,
60
- close,
61
- update,
62
- isOpen,
63
- getCurrentId,
64
- };
65
- };
66
-
1
+ import type { OverlayLoadingOptions } from "../types/overlay-loading.types";
2
+
3
+ export const useOverlayLoading = () => {
4
+ const overlay = useOverlay();
5
+ const OverlayLoadingComponent = defineAsyncComponent(
6
+ () => import("../components/OverlayLoading.vue")
7
+ );
8
+
9
+ let currentOverlay: {
10
+ id: symbol;
11
+ open: () => void;
12
+ close: () => void;
13
+ patch: (props: Record<string, unknown>) => void;
14
+ } | null = null;
15
+ const show = (options: OverlayLoadingOptions) => {
16
+ if (currentOverlay) {
17
+ currentOverlay.close();
18
+ }
19
+ currentOverlay = overlay.create(OverlayLoadingComponent, {
20
+ destroyOnClose: true,
21
+ props: {
22
+ isVisible: true,
23
+ ...options,
24
+ },
25
+ });
26
+
27
+ currentOverlay?.open();
28
+
29
+ return currentOverlay?.id ?? null;
30
+ };
31
+
32
+ const close = () => {
33
+ if (currentOverlay) {
34
+ currentOverlay.close();
35
+ currentOverlay = null;
36
+ }
37
+ };
38
+
39
+ /**
40
+ * Mescla props no componente do overlay. Props devem ser planas (title, description, …):
41
+ * o `patch` do useOverlay faz `{ ...overlay.props, ...patch }`, não aceita `{ props: { … } }`.
42
+ */
43
+ const update = (options: Partial<OverlayLoadingOptions>) => {
44
+ if (currentOverlay) {
45
+ currentOverlay.patch({
46
+ isVisible: true,
47
+ ...options,
48
+ });
49
+ }
50
+ };
51
+
52
+ const isOpen = () => {
53
+ return currentOverlay !== null;
54
+ };
55
+
56
+ const getCurrentId = () => currentOverlay?.id ?? null;
57
+
58
+ return {
59
+ show,
60
+ close,
61
+ update,
62
+ isOpen,
63
+ getCurrentId,
64
+ };
65
+ };
66
+
@@ -1,6 +1,6 @@
1
- import { pt_br } from "@nuxt/ui/locale";
2
-
3
- /** Locale do Nuxt UI para calendário, meses e rótulos em português do Brasil. */
4
- export const uiLocale = pt_br;
5
-
6
- export const UI_LOCALE_CODE = "pt-BR";
1
+ import { pt_br } from "@nuxt/ui/locale";
2
+
3
+ /** Locale do Nuxt UI para calendário, meses e rótulos em português do Brasil. */
4
+ export const uiLocale = pt_br;
5
+
6
+ export const UI_LOCALE_CODE = "pt-BR";