@apptegy/nuxt-navigation 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.mts +7 -0
- package/dist/module.d.ts +7 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +22 -0
- package/dist/runtime/assets/edurooms-logo-white.svg +1 -0
- package/dist/runtime/assets/edurooms-logo.svg +1 -0
- package/dist/runtime/assets/icons/24/Minus.svg +5 -0
- package/dist/runtime/assets/icons/24/Plus.svg +5 -0
- package/dist/runtime/assets/icons/32/logout.svg +4 -0
- package/dist/runtime/assets/img/help-toggle.svg +13 -0
- package/dist/runtime/assets/img/help.svg +5 -0
- package/dist/runtime/assets/img/impersonate-user.svg +3 -0
- package/dist/runtime/assets/img/navbar-minus.svg +3 -0
- package/dist/runtime/assets/img/navbar-plus.svg +3 -0
- package/dist/runtime/assets/img/navbar-up.svg +3 -0
- package/dist/runtime/assets/img/user.svg +8 -0
- package/dist/runtime/assets/logo.svg +56 -0
- package/dist/runtime/assets/thrillshare-icon-white.svg +1 -0
- package/dist/runtime/assets/thrillshare-logo-white.svg +1 -0
- package/dist/runtime/components/Nav/Avatar.vue +61 -0
- package/dist/runtime/components/Nav/Breadcrumb.vue +23 -0
- package/dist/runtime/components/Nav/Header.vue +200 -0
- package/dist/runtime/components/Nav/HelpArticles.vue +154 -0
- package/dist/runtime/components/Nav/ImpersonateInfoPanel.vue +135 -0
- package/dist/runtime/components/Nav/OrgSelect.vue +198 -0
- package/dist/runtime/components/Nav/Sidebar.vue +187 -0
- package/dist/runtime/components/Nav/SidebarItem.vue +40 -0
- package/dist/runtime/components/Nav/SidebarSection.vue +73 -0
- package/dist/runtime/components/Nav/SubMenu.vue +37 -0
- package/dist/runtime/components/Nav/SubMenuItem.vue +66 -0
- package/dist/runtime/composables/accessibleActions.d.ts +33 -0
- package/dist/runtime/composables/accessibleActions.js +195 -0
- package/dist/types.d.mts +1 -0
- package/dist/types.d.ts +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="a-nav-section" :class="{ active }">
|
|
3
|
+
<NuxtLink class="a-nav-section--link" :to="application.url">
|
|
4
|
+
<img v-if="application.img_src" :src="iconUrl" alt="icon">
|
|
5
|
+
<span>{{ application.text.replace("_", " ") }}</span>
|
|
6
|
+
<a-icon v-if="active" class="caret-icon" icon="16:carret-up" />
|
|
7
|
+
</NuxtLink>
|
|
8
|
+
<div class="a-nav-section--content">
|
|
9
|
+
<slot />
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { computed } from 'vue';
|
|
16
|
+
|
|
17
|
+
const props = defineProps<{
|
|
18
|
+
application: IApplication;
|
|
19
|
+
active: boolean;
|
|
20
|
+
navbarExpanded: boolean;
|
|
21
|
+
}>();
|
|
22
|
+
|
|
23
|
+
const iconUrl = computed(() =>
|
|
24
|
+
props.active
|
|
25
|
+
? props.application.img_src?.replace(".svg", "_active.svg")
|
|
26
|
+
: props.application.img_src,
|
|
27
|
+
);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<style scoped>
|
|
31
|
+
.a-nav-section {
|
|
32
|
+
box-shadow: inset 0px -1px 0px 0px var(--dscl-nav-section-border-bottom-color, var(--purple-65));
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
min-height: 48px;
|
|
35
|
+
/* &--content {
|
|
36
|
+
margin-bottom: 8px;
|
|
37
|
+
} */
|
|
38
|
+
}
|
|
39
|
+
.a-nav-section.active {
|
|
40
|
+
box-shadow: inset 0px -1px 0px 0px var(--dscl-nav-section-border-bottom-color, var(--purple-65));
|
|
41
|
+
padding-bottom: 4px;
|
|
42
|
+
}
|
|
43
|
+
.a-nav-section--link {
|
|
44
|
+
display: flex;
|
|
45
|
+
color: var(--dscl-nav-font-color, var(--dscl-light-font, var(--primary-white)));
|
|
46
|
+
text-decoration: none;
|
|
47
|
+
min-height: 48px;
|
|
48
|
+
align-items: center;
|
|
49
|
+
font-size: 12px;
|
|
50
|
+
letter-spacing: 1.4px;
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
padding: 0px 16px;
|
|
53
|
+
width: 100%;
|
|
54
|
+
}
|
|
55
|
+
.a-nav-section--link:focus-visible {
|
|
56
|
+
outline: var(--orange) auto 2px;
|
|
57
|
+
outline-offset: 0px;
|
|
58
|
+
box-shadow: inset 0 0 0px 4px var(--primary-white);
|
|
59
|
+
}
|
|
60
|
+
.a-nav-section--link span {
|
|
61
|
+
margin: 0 8px;
|
|
62
|
+
white-space: nowrap;
|
|
63
|
+
text-transform: uppercase;
|
|
64
|
+
width: inherit;
|
|
65
|
+
}
|
|
66
|
+
.a-nav-section--link .caret-icon {
|
|
67
|
+
font-size: 24px;
|
|
68
|
+
transition: transform 0.2s ease-in-out;
|
|
69
|
+
}
|
|
70
|
+
.a-nav-section--link .caret-icon.collapse {
|
|
71
|
+
transform: rotate(0.5turn);
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="secondary-nav">
|
|
3
|
+
<div class="item-wrapper">
|
|
4
|
+
<slot />
|
|
5
|
+
</div>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup lang="ts">
|
|
10
|
+
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<style scoped>
|
|
14
|
+
.secondary-nav {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
width: 100%;
|
|
18
|
+
margin-bottom: 20px;
|
|
19
|
+
background-color: var(--secondary-nav-background, var(--surface-0, var(--primary-white)));
|
|
20
|
+
color: var(--secondary-nav-text, var(--brand-text, var(--primary-dark)));
|
|
21
|
+
border-radius: 4px;
|
|
22
|
+
box-shadow: var(--secondary-nav-shadow, var(--shadow-6));
|
|
23
|
+
transition: box-shadow 0.3s ease-in-out;
|
|
24
|
+
}
|
|
25
|
+
.secondary-nav:before {
|
|
26
|
+
content: "";
|
|
27
|
+
margin: 4px;
|
|
28
|
+
height: 2px;
|
|
29
|
+
border-radius: 2px;
|
|
30
|
+
background-color: var(--seconadry-nav-accent, var(--brand-0, var(--purple)));
|
|
31
|
+
}
|
|
32
|
+
.secondary-nav .item-wrapper {
|
|
33
|
+
display: flex;
|
|
34
|
+
gap: 4px;
|
|
35
|
+
padding: 12px 24px 0;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps({
|
|
3
|
+
icon: {
|
|
4
|
+
type: String,
|
|
5
|
+
required: true
|
|
6
|
+
},
|
|
7
|
+
})
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<NuxtLink class="menu-item">
|
|
13
|
+
<div class="menu-item__icon">
|
|
14
|
+
<a-icon :icon />
|
|
15
|
+
</div>
|
|
16
|
+
<span class="menu-item__text">
|
|
17
|
+
<slot />
|
|
18
|
+
</span>
|
|
19
|
+
</NuxtLink>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<style scoped>
|
|
23
|
+
.menu-item {
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
color: var(--primary-dark);
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
justify-content: flex-end;
|
|
29
|
+
align-items: center;
|
|
30
|
+
padding: 12px 16px;
|
|
31
|
+
padding-bottom: 4px;
|
|
32
|
+
text-decoration: none;
|
|
33
|
+
}
|
|
34
|
+
.menu-item__text {
|
|
35
|
+
font: var(--font-regular);
|
|
36
|
+
margin-bottom: 4px;
|
|
37
|
+
}
|
|
38
|
+
.menu-item__icon {
|
|
39
|
+
font-size: 32px;
|
|
40
|
+
margin-bottom: 4px;
|
|
41
|
+
}
|
|
42
|
+
.menu-item:after {
|
|
43
|
+
content: "";
|
|
44
|
+
bottom: 0;
|
|
45
|
+
right: 0;
|
|
46
|
+
height: 0;
|
|
47
|
+
width: 16px;
|
|
48
|
+
border-top: 2px solid transparent;
|
|
49
|
+
border-radius: 4px;
|
|
50
|
+
}
|
|
51
|
+
.menu-item:hover, .menu-item:focus, .menu-item.nuxt-link-exact-active, .menu-item.router-link-exact-active {
|
|
52
|
+
background-color: var(--purple-10);
|
|
53
|
+
border-radius: 4px 4px 0px 0px;
|
|
54
|
+
color: var(--purple);
|
|
55
|
+
outline: none;
|
|
56
|
+
}
|
|
57
|
+
.menu-item:hover:after, .menu-item:focus:after, .menu-item.nuxt-link-exact-active:after, .menu-item.router-link-exact-active:after {
|
|
58
|
+
border-top: 2px solid var(--purple);
|
|
59
|
+
}
|
|
60
|
+
.menu-item.nuxt-link-exact-active, .menu-item.router-link-exact-active {
|
|
61
|
+
cursor: default;
|
|
62
|
+
}
|
|
63
|
+
.menu-item.nuxt-link-exact-active .menu-item__text, .menu-item.router-link-exact-active .menu-item__text {
|
|
64
|
+
font: var(--font-medium);
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare function useDropdown(): {
|
|
2
|
+
active: any;
|
|
3
|
+
newQuery: any;
|
|
4
|
+
toggleDropdown: ($event: {
|
|
5
|
+
key: string;
|
|
6
|
+
}, optionRef: {
|
|
7
|
+
focus: () => void;
|
|
8
|
+
}[]) => Promise<void>;
|
|
9
|
+
openDropdown: () => void;
|
|
10
|
+
closeDropdown: () => void;
|
|
11
|
+
onClickOutsideHandler: () => void;
|
|
12
|
+
keydownActionsOnTrigger: (event: {
|
|
13
|
+
key: string;
|
|
14
|
+
preventDefault: () => void;
|
|
15
|
+
}, options: any[], optionRef: {
|
|
16
|
+
focus: () => void;
|
|
17
|
+
}[], dropdownTriggerRef: {
|
|
18
|
+
focus: () => void;
|
|
19
|
+
}, searchable: false, inputRef: {
|
|
20
|
+
focus: () => void;
|
|
21
|
+
}) => void;
|
|
22
|
+
handleKeydown: (options: any[], optionIndex: number, event: {
|
|
23
|
+
key: string;
|
|
24
|
+
}, optionRef: {
|
|
25
|
+
focus: () => void;
|
|
26
|
+
}[], triggerRef: {
|
|
27
|
+
focus: () => void;
|
|
28
|
+
}) => number | undefined;
|
|
29
|
+
handleDropdownSearch: (val: string, orgs: any[], optionRef: {
|
|
30
|
+
focus: () => void;
|
|
31
|
+
}[]) => void;
|
|
32
|
+
debouncedFocus: any;
|
|
33
|
+
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import debounce from "lodash/debounce";
|
|
2
|
+
import { ref, nextTick } from "vue";
|
|
3
|
+
export function useDropdown() {
|
|
4
|
+
const active = ref(false);
|
|
5
|
+
let newQuery = ref("");
|
|
6
|
+
async function toggleDropdown($event, optionRef) {
|
|
7
|
+
active.value = !active.value;
|
|
8
|
+
if (active.value && $event.key === "ArrowDown") {
|
|
9
|
+
await nextTick(() => {
|
|
10
|
+
optionRef[0].focus();
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function openDropdown() {
|
|
15
|
+
active.value = true;
|
|
16
|
+
}
|
|
17
|
+
function closeDropdown() {
|
|
18
|
+
active.value = false;
|
|
19
|
+
}
|
|
20
|
+
function onClickOutsideHandler() {
|
|
21
|
+
if (!active.value)
|
|
22
|
+
return;
|
|
23
|
+
closeDropdown();
|
|
24
|
+
}
|
|
25
|
+
function keydownActionsOnTrigger(event, options, optionRef, dropdownTriggerRef, searchable, inputRef) {
|
|
26
|
+
if (event.key !== "Tab") {
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
}
|
|
29
|
+
switch (event.key) {
|
|
30
|
+
case "Tab":
|
|
31
|
+
if (!active.value)
|
|
32
|
+
return;
|
|
33
|
+
closeDropdown();
|
|
34
|
+
break;
|
|
35
|
+
case "ArrowDown":
|
|
36
|
+
nextTick(() => {
|
|
37
|
+
if (active.value && event.key === "ArrowDown") {
|
|
38
|
+
optionRef[0].focus();
|
|
39
|
+
}
|
|
40
|
+
openDropdown();
|
|
41
|
+
});
|
|
42
|
+
break;
|
|
43
|
+
case "ArrowUp":
|
|
44
|
+
openDropdown();
|
|
45
|
+
nextTick(() => {
|
|
46
|
+
if (searchable) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
optionRef[0].focus();
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
case " ":
|
|
53
|
+
if (active.value) {
|
|
54
|
+
closeDropdown();
|
|
55
|
+
nextTick(() => {
|
|
56
|
+
dropdownTriggerRef.focus();
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
openDropdown();
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
case "Enter":
|
|
63
|
+
if (active.value) {
|
|
64
|
+
closeDropdown();
|
|
65
|
+
nextTick(() => {
|
|
66
|
+
dropdownTriggerRef.focus();
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
nextTick(() => {
|
|
70
|
+
openDropdown();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
case "Escape":
|
|
75
|
+
closeDropdown();
|
|
76
|
+
dropdownTriggerRef.focus();
|
|
77
|
+
break;
|
|
78
|
+
case "Home":
|
|
79
|
+
openDropdown();
|
|
80
|
+
nextTick(() => {
|
|
81
|
+
optionRef[0].focus();
|
|
82
|
+
});
|
|
83
|
+
break;
|
|
84
|
+
case "End":
|
|
85
|
+
openDropdown();
|
|
86
|
+
nextTick(() => {
|
|
87
|
+
optionRef[options.length - 1].focus();
|
|
88
|
+
});
|
|
89
|
+
break;
|
|
90
|
+
default:
|
|
91
|
+
debounce(handleDropdownSearch(event.key, options, optionRef), 500);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function handleKeydown(options, optionIndex, event, optionRef, triggerRef) {
|
|
96
|
+
if (event.altKey && event.key === "ArrowUp") {
|
|
97
|
+
closeDropdown();
|
|
98
|
+
triggerRef.focus();
|
|
99
|
+
return optionIndex;
|
|
100
|
+
}
|
|
101
|
+
switch (event.key) {
|
|
102
|
+
case "Enter":
|
|
103
|
+
nextTick(() => {
|
|
104
|
+
closeDropdown();
|
|
105
|
+
triggerRef.focus();
|
|
106
|
+
return optionIndex;
|
|
107
|
+
});
|
|
108
|
+
break;
|
|
109
|
+
case " ":
|
|
110
|
+
nextTick(() => {
|
|
111
|
+
closeDropdown();
|
|
112
|
+
triggerRef.focus();
|
|
113
|
+
return optionIndex;
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
116
|
+
case "Tab":
|
|
117
|
+
nextTick(() => {
|
|
118
|
+
closeDropdown();
|
|
119
|
+
return optionIndex;
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
case "Escape":
|
|
123
|
+
nextTick(() => {
|
|
124
|
+
closeDropdown();
|
|
125
|
+
triggerRef.focus();
|
|
126
|
+
});
|
|
127
|
+
break;
|
|
128
|
+
case "ArrowDown":
|
|
129
|
+
if (optionIndex === options.length - 1)
|
|
130
|
+
break;
|
|
131
|
+
nextTick(() => {
|
|
132
|
+
optionRef[optionIndex + 1].focus();
|
|
133
|
+
});
|
|
134
|
+
return optionIndex;
|
|
135
|
+
case "ArrowUp":
|
|
136
|
+
if (optionIndex === 0)
|
|
137
|
+
break;
|
|
138
|
+
nextTick(() => {
|
|
139
|
+
optionRef[optionIndex - 1].focus();
|
|
140
|
+
});
|
|
141
|
+
return optionIndex;
|
|
142
|
+
case "Home":
|
|
143
|
+
nextTick(() => {
|
|
144
|
+
optionRef[0].focus();
|
|
145
|
+
});
|
|
146
|
+
return optionIndex;
|
|
147
|
+
case "End":
|
|
148
|
+
nextTick(() => {
|
|
149
|
+
optionRef[options.length - 1].focus();
|
|
150
|
+
});
|
|
151
|
+
return optionIndex;
|
|
152
|
+
case "PageDown": {
|
|
153
|
+
const pageDownIndexToJump = optionIndex + 10 > options.length ? optionIndex + 10 : options.length - 1;
|
|
154
|
+
optionRef[pageDownIndexToJump].focus();
|
|
155
|
+
return optionIndex;
|
|
156
|
+
}
|
|
157
|
+
case "PageUp": {
|
|
158
|
+
const pageUpIndexToJump = optionIndex - 10 < 0 ? optionIndex - 10 : 0;
|
|
159
|
+
optionRef[pageUpIndexToJump].focus();
|
|
160
|
+
return optionIndex;
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
handleDropdownSearch(event.key, options, optionRef);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const debouncedFocus = debounce((idx, optionRef) => {
|
|
168
|
+
optionRef[idx].focus();
|
|
169
|
+
}, 500);
|
|
170
|
+
function handleDropdownSearch(val, orgs, optionRef) {
|
|
171
|
+
if (val == "Backspace") {
|
|
172
|
+
newQuery.value = newQuery.value.slice(0, -1);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
newQuery.value = `${newQuery.value}${val}`;
|
|
176
|
+
const searchIndex = orgs.findIndex(
|
|
177
|
+
(option) => option.name.toLowerCase().includes(newQuery.value.toLowerCase())
|
|
178
|
+
);
|
|
179
|
+
if (searchIndex !== -1) {
|
|
180
|
+
debouncedFocus(searchIndex, optionRef);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
active,
|
|
185
|
+
newQuery,
|
|
186
|
+
toggleDropdown,
|
|
187
|
+
openDropdown,
|
|
188
|
+
closeDropdown,
|
|
189
|
+
onClickOutsideHandler,
|
|
190
|
+
keydownActionsOnTrigger,
|
|
191
|
+
handleKeydown,
|
|
192
|
+
handleDropdownSearch,
|
|
193
|
+
debouncedFocus
|
|
194
|
+
};
|
|
195
|
+
}
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type ModuleOptions, default } from './module.js'
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type ModuleOptions, default } from './module'
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@apptegy/nuxt-navigation",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/types.d.ts",
|
|
8
|
+
"import": "./dist/module.mjs",
|
|
9
|
+
"require": "./dist/module.cjs"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/module.cjs",
|
|
13
|
+
"types": "./dist/types.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"prepack": "nuxt-module-build build",
|
|
19
|
+
"dev": "nuxi dev playground",
|
|
20
|
+
"dev:build": "nuxi build playground",
|
|
21
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
22
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
23
|
+
"lint": "eslint .",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest watch",
|
|
26
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@nuxt/kit": "^3.12.3"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@nuxt/devtools": "^1.3.9",
|
|
33
|
+
"@nuxt/eslint-config": "^0.3.13",
|
|
34
|
+
"@apptegy/icons": "2.10.0",
|
|
35
|
+
"@nuxt/module-builder": "^0.8.1",
|
|
36
|
+
"@nuxt/schema": "^3.12.3",
|
|
37
|
+
"@nuxt/test-utils": "^3.13.1",
|
|
38
|
+
"@types/node": "^20.14.10",
|
|
39
|
+
"changelogen": "^0.5.5",
|
|
40
|
+
"eslint": "^9.7.0",
|
|
41
|
+
"lodash": "^4.17.15",
|
|
42
|
+
"nuxt": "^3.12.3",
|
|
43
|
+
"sass": "^1.77.8",
|
|
44
|
+
"typescript": "^5.5.3",
|
|
45
|
+
"vitest": "^1.6.0",
|
|
46
|
+
"vue": "^3.4.21",
|
|
47
|
+
"vue-tsc": "^2.0.26"
|
|
48
|
+
}
|
|
49
|
+
}
|