@shwfed/nuxt 0.1.74 → 0.1.76
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/dist/module.d.mts +13 -134
- package/dist/module.json +2 -2
- package/dist/module.mjs +17 -14
- package/dist/runtime/components/app.d.vue.ts +33 -3
- package/dist/runtime/components/app.vue +77 -304
- package/dist/runtime/components/app.vue.d.ts +33 -3
- package/dist/runtime/composables/useFavorite.d.ts +1 -1
- package/dist/runtime/composables/useNavigationTabs.d.ts +19 -5
- package/dist/runtime/composables/useNavigationTabs.js +71 -46
- package/dist/runtime/middleware/token.d.ts +2 -0
- package/dist/runtime/middleware/token.js +9 -0
- package/dist/runtime/plugins/api/index.d.ts +0 -2
- package/dist/runtime/plugins/api/index.js +6 -56
- package/dist/runtime/plugins/cel/env.js +1 -1
- package/dist/runtime/plugins/cel/index.js +3 -2
- package/dist/runtime/plugins/toast/index.d.ts +2 -2
- package/dist/types.d.mts +7 -1
- package/package.json +1 -1
- package/dist/runtime/utils/command-palette.d.ts +0 -71
- package/dist/runtime/utils/command-palette.js +0 -72
- package/dist/runtime/utils/command.d.ts +0 -31
- package/dist/runtime/utils/command.js +0 -40
- package/dist/runtime/utils/navigation-title.d.ts +0 -10
- package/dist/runtime/utils/navigation-title.js +0 -28
- package/dist/runtime/utils/route.d.ts +0 -2
- package/dist/runtime/utils/route.js +0 -14
- package/dist/runtime/utils/sidebar-fallback.d.ts +0 -16
- package/dist/runtime/utils/sidebar-fallback.js +0 -32
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { useHead, useNuxtApp
|
|
3
|
-
import {
|
|
2
|
+
import { useHead, useNuxtApp } from "#app";
|
|
3
|
+
import { reactive } from "vue";
|
|
4
4
|
import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "./ui/command";
|
|
5
5
|
import { TooltipProvider } from "./ui/tooltip";
|
|
6
6
|
import { Toaster } from "vue-sonner";
|
|
@@ -12,26 +12,23 @@ import { setGlobalDslContext } from "../plugins/cel/context";
|
|
|
12
12
|
import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarProvider } from "./ui/sidebar";
|
|
13
13
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible";
|
|
14
14
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "./ui/dropdown-menu";
|
|
15
|
-
import { z } from "zod";
|
|
16
15
|
import Logo from "./logo.vue";
|
|
17
16
|
import { useFavorite } from "../composables/useFavorite";
|
|
18
17
|
import { useNavigationTabs } from "../composables/useNavigationTabs";
|
|
19
|
-
|
|
20
|
-
import { createPaletteItemValue, normalizeCommandsForPalette, splitNavigationForPalette } from "../utils/command-palette";
|
|
21
|
-
import { resolveNavigationTitle } from "../utils/navigation-title";
|
|
22
|
-
import { isRouteActive } from "../utils/route";
|
|
23
|
-
import { applyRootNavigationFallback } from "../utils/sidebar-fallback";
|
|
24
|
-
const { $dsl, $logout, $md, $api, $toast } = useNuxtApp();
|
|
18
|
+
const { $dsl } = useNuxtApp();
|
|
25
19
|
const { t } = useI18n();
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
const props = defineProps({
|
|
21
|
+
dsl: { type: null, required: false },
|
|
22
|
+
sidebar: { type: Array, required: false },
|
|
23
|
+
commands: { type: Array, required: false }
|
|
24
|
+
});
|
|
25
|
+
const { active, tabs, nameOf, close } = useNavigationTabs(() => props.sidebar ?? []);
|
|
26
|
+
const ui = reactive({
|
|
27
|
+
isCommandPaletteOpen: false,
|
|
28
|
+
isProfileDropdownOpen: false
|
|
29
|
+
});
|
|
33
30
|
useHead({
|
|
34
|
-
title: () => active.value &&
|
|
31
|
+
title: () => active.value && nameOf(active.value),
|
|
35
32
|
bodyAttrs: {
|
|
36
33
|
style: {
|
|
37
34
|
"--primary": "#2DA8BC",
|
|
@@ -39,213 +36,18 @@ useHead({
|
|
|
39
36
|
}
|
|
40
37
|
}
|
|
41
38
|
});
|
|
42
|
-
const props = defineProps({
|
|
43
|
-
dsl: { type: null, required: false }
|
|
44
|
-
});
|
|
45
|
-
const ui = reactive({
|
|
46
|
-
isCommandPaletteOpen: false,
|
|
47
|
-
isProfileDropdownOpen: false
|
|
48
|
-
});
|
|
49
39
|
const { start: startProfileCloseTimer, stop: stopProfileCloseTimer } = useTimeoutFn(() => {
|
|
50
40
|
ui.isProfileDropdownOpen = false;
|
|
51
41
|
}, 150, { immediate: false });
|
|
52
42
|
const { meta_k } = useMagicKeys();
|
|
53
43
|
whenever(() => meta_k?.value, () => ui.isCommandPaletteOpen = !ui.isCommandPaletteOpen);
|
|
54
44
|
setGlobalDslContext(await props.dsl?.pipe(Effect.scoped).pipe(Effect.runPromise) ?? {});
|
|
55
|
-
const navigationItem = z.object({
|
|
56
|
-
id: z.union([z.string(), z.number()]),
|
|
57
|
-
title: z.string(),
|
|
58
|
-
icon: z.string().optional(),
|
|
59
|
-
route: z.string(),
|
|
60
|
-
keywords: z.array(z.string()).optional()
|
|
61
|
-
});
|
|
62
|
-
const navigationGroup = z.object({
|
|
63
|
-
id: z.union([z.string(), z.number()]),
|
|
64
|
-
title: z.string(),
|
|
65
|
-
icon: z.string().optional(),
|
|
66
|
-
children: z.array(navigationItem)
|
|
67
|
-
});
|
|
68
|
-
const navigationEntry = z.union([navigationGroup, navigationItem]);
|
|
69
|
-
const navigationItems = computed(() => {
|
|
70
|
-
try {
|
|
71
|
-
const result = $dsl.evaluate`${config.sidebar.menus}`();
|
|
72
|
-
return z.array(navigationEntry).parse(result);
|
|
73
|
-
} catch {
|
|
74
|
-
return [];
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
45
|
const {
|
|
78
46
|
isFavorited: isNavigationFavorited,
|
|
79
47
|
canBeFavorited: canBeNavigationFavorited,
|
|
80
48
|
toggle: toggleNavigationFavorite,
|
|
81
49
|
withFavorites: navigations
|
|
82
|
-
} = useFavorite("navigation",
|
|
83
|
-
const isNavigationItemActive = (itemRoute) => isRouteActive(route.path, itemRoute);
|
|
84
|
-
const getTabLabel = (tab) => {
|
|
85
|
-
return resolveNavigationTitle(navigationItems.value, tab);
|
|
86
|
-
};
|
|
87
|
-
const profileName = computed(() => {
|
|
88
|
-
if (config.profile.name === void 0)
|
|
89
|
-
return t("profile");
|
|
90
|
-
try {
|
|
91
|
-
const content = $md.inline`${config.profile.name}`();
|
|
92
|
-
const plainText = content.replace(/<[^>]*>/g, "").trim();
|
|
93
|
-
if (plainText.length === 0)
|
|
94
|
-
return t("profile");
|
|
95
|
-
return plainText;
|
|
96
|
-
} catch {
|
|
97
|
-
return t("profile");
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
const commandItem = z.object({
|
|
101
|
-
id: z.union([z.string(), z.number()]),
|
|
102
|
-
title: z.string(),
|
|
103
|
-
icon: z.string().optional(),
|
|
104
|
-
hidden: z.string().optional(),
|
|
105
|
-
disabled: z.string().optional(),
|
|
106
|
-
effect: z.string().optional(),
|
|
107
|
-
keywords: z.array(z.string()).optional()
|
|
108
|
-
});
|
|
109
|
-
const commandGroup = z.object({
|
|
110
|
-
id: z.union([z.string(), z.number()]),
|
|
111
|
-
title: z.string(),
|
|
112
|
-
icon: z.string().optional(),
|
|
113
|
-
children: z.array(commandItem)
|
|
114
|
-
});
|
|
115
|
-
const evaluateDslBoolean = (expression) => {
|
|
116
|
-
if (expression === void 0)
|
|
117
|
-
return false;
|
|
118
|
-
try {
|
|
119
|
-
return $dsl.evaluate`${expression}`() === true;
|
|
120
|
-
} catch {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
const normalizeEffectExpression = (expression) => {
|
|
125
|
-
return expression.replace(/([{,]\s*)([a-z_][\w-]*)(\s*:)/gi, "$1'$2'$3");
|
|
126
|
-
};
|
|
127
|
-
const profileCommands = computed(() => {
|
|
128
|
-
if (config.commands === void 0)
|
|
129
|
-
return [];
|
|
130
|
-
try {
|
|
131
|
-
const result = $dsl.evaluate`${config.commands}`();
|
|
132
|
-
const entries = z.array(z.union([commandGroup, commandItem])).parse(result);
|
|
133
|
-
const normalized = [];
|
|
134
|
-
for (const entry of entries) {
|
|
135
|
-
if ("children" in entry) {
|
|
136
|
-
const children = entry.children.filter((child) => !evaluateDslBoolean(child.hidden)).map((child) => ({
|
|
137
|
-
id: child.id,
|
|
138
|
-
title: child.title,
|
|
139
|
-
icon: child.icon,
|
|
140
|
-
disabled: evaluateDslBoolean(child.disabled),
|
|
141
|
-
effect: child.effect,
|
|
142
|
-
keywords: child.keywords
|
|
143
|
-
}));
|
|
144
|
-
if (children.length > 0) {
|
|
145
|
-
normalized.push({
|
|
146
|
-
id: entry.id,
|
|
147
|
-
title: entry.title,
|
|
148
|
-
icon: entry.icon,
|
|
149
|
-
children
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (evaluateDslBoolean(entry.hidden))
|
|
155
|
-
continue;
|
|
156
|
-
normalized.push({
|
|
157
|
-
id: entry.id,
|
|
158
|
-
title: entry.title,
|
|
159
|
-
icon: entry.icon,
|
|
160
|
-
disabled: evaluateDslBoolean(entry.disabled),
|
|
161
|
-
effect: entry.effect,
|
|
162
|
-
keywords: entry.keywords
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
return normalized;
|
|
166
|
-
} catch {
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
const isBodyInit = (value) => {
|
|
171
|
-
return typeof value === "string" || value instanceof Blob || value instanceof ArrayBuffer || ArrayBuffer.isView(value) || value instanceof FormData || value instanceof URLSearchParams || value instanceof ReadableStream;
|
|
172
|
-
};
|
|
173
|
-
const toApiRequestBody = (value) => {
|
|
174
|
-
if (value === void 0 || value === null)
|
|
175
|
-
return value;
|
|
176
|
-
if (isBodyInit(value))
|
|
177
|
-
return value;
|
|
178
|
-
if (typeof value === "object")
|
|
179
|
-
return value;
|
|
180
|
-
return void 0;
|
|
181
|
-
};
|
|
182
|
-
const executeCommandEffect = async (effect) => {
|
|
183
|
-
if (effect === void 0)
|
|
184
|
-
return false;
|
|
185
|
-
let action;
|
|
186
|
-
try {
|
|
187
|
-
let result;
|
|
188
|
-
try {
|
|
189
|
-
result = $dsl.evaluate`${effect}`();
|
|
190
|
-
} catch {
|
|
191
|
-
result = $dsl.evaluate`${normalizeEffectExpression(effect)}`();
|
|
192
|
-
}
|
|
193
|
-
action = commandActionSchema.parse(result);
|
|
194
|
-
await executeCommandAction(action, {
|
|
195
|
-
logout: $logout,
|
|
196
|
-
request: async (requestAction) => $api(requestAction.url, {
|
|
197
|
-
method: requestAction.method,
|
|
198
|
-
body: toApiRequestBody(requestAction.body),
|
|
199
|
-
query: requestAction.query,
|
|
200
|
-
headers: requestAction.headers
|
|
201
|
-
})
|
|
202
|
-
});
|
|
203
|
-
return true;
|
|
204
|
-
} catch (error) {
|
|
205
|
-
if (action?.type === "request") {
|
|
206
|
-
const message = error instanceof Error && error.message.length > 0 ? error.message : "Request failed";
|
|
207
|
-
$toast.error(message);
|
|
208
|
-
}
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
const executeProfileCommand = async (effect) => {
|
|
213
|
-
if (effect === void 0 || await executeCommandEffect(effect))
|
|
214
|
-
ui.isProfileDropdownOpen = false;
|
|
215
|
-
};
|
|
216
|
-
const normalizedNavigationForPalette = computed(() => {
|
|
217
|
-
try {
|
|
218
|
-
return z.array(navigationEntry).parse(navigations.value);
|
|
219
|
-
} catch {
|
|
220
|
-
return navigationItems.value;
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
const paletteNavigation = computed(() => splitNavigationForPalette(normalizedNavigationForPalette.value));
|
|
224
|
-
const favoriteRoutesForPalette = computed(() => paletteNavigation.value.favoriteRoutes);
|
|
225
|
-
const navigationForPalette = computed(() => paletteNavigation.value.navigationEntries);
|
|
226
|
-
const commandForPalette = computed(() => normalizeCommandsForPalette(profileCommands.value));
|
|
227
|
-
const executePaletteItem = async (item) => {
|
|
228
|
-
if (item.type === "route") {
|
|
229
|
-
activateTab(item.route);
|
|
230
|
-
ui.isCommandPaletteOpen = false;
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
if (item.effect !== void 0 && !await executeCommandEffect(item.effect))
|
|
234
|
-
return;
|
|
235
|
-
ui.isCommandPaletteOpen = false;
|
|
236
|
-
};
|
|
237
|
-
watch(
|
|
238
|
-
() => route.path,
|
|
239
|
-
(path) => {
|
|
240
|
-
applyRootNavigationFallback({
|
|
241
|
-
path,
|
|
242
|
-
items: navigationItems.value,
|
|
243
|
-
tabs: tabs.value,
|
|
244
|
-
activateTab
|
|
245
|
-
});
|
|
246
|
-
},
|
|
247
|
-
{ immediate: true }
|
|
248
|
-
);
|
|
50
|
+
} = useFavorite("navigation", () => props.sidebar ?? []);
|
|
249
51
|
</script>
|
|
250
52
|
|
|
251
53
|
<template>
|
|
@@ -269,102 +71,68 @@ watch(
|
|
|
269
71
|
</section>
|
|
270
72
|
</CommandEmpty>
|
|
271
73
|
|
|
272
|
-
<CommandGroup
|
|
273
|
-
v-if="favoriteRoutesForPalette.length > 0"
|
|
274
|
-
:heading="t('favorites')"
|
|
275
|
-
>
|
|
276
|
-
<CommandItem
|
|
277
|
-
v-for="menu in favoriteRoutesForPalette"
|
|
278
|
-
:key="`fav:${menu.id}:${menu.route}`"
|
|
279
|
-
:value="createPaletteItemValue('fav', `${menu.id}:${menu.route}`)"
|
|
280
|
-
@select="executePaletteItem({ type: 'route', route: menu.route })"
|
|
281
|
-
>
|
|
282
|
-
<span>{{ menu.title }}</span>
|
|
283
|
-
<span
|
|
284
|
-
v-if="menu.keywords.length > 0"
|
|
285
|
-
class="sr-only"
|
|
286
|
-
>
|
|
287
|
-
{{ menu.keywords.join(" ") }}
|
|
288
|
-
</span>
|
|
289
|
-
</CommandItem>
|
|
290
|
-
</CommandGroup>
|
|
291
|
-
|
|
292
74
|
<template
|
|
293
|
-
v-for="
|
|
294
|
-
:key="
|
|
75
|
+
v-for="command in props.commands"
|
|
76
|
+
:key="command.id"
|
|
295
77
|
>
|
|
296
78
|
<CommandGroup
|
|
297
|
-
v-if="'children' in
|
|
298
|
-
:heading="
|
|
79
|
+
v-if="'children' in command"
|
|
80
|
+
:heading="command.title"
|
|
299
81
|
>
|
|
300
82
|
<CommandItem
|
|
301
|
-
v-for="
|
|
302
|
-
:key="
|
|
303
|
-
:value="
|
|
304
|
-
|
|
83
|
+
v-for="child in command.children.filter((child2) => !child2.hidden)"
|
|
84
|
+
:key="child.id"
|
|
85
|
+
:value="child.id"
|
|
86
|
+
:disabled="child.disabled"
|
|
87
|
+
@select="child.effect?.pipe(Effect.tap(() => {
|
|
88
|
+
ui.isCommandPaletteOpen = false;
|
|
89
|
+
})).pipe(Effect.runPromise)"
|
|
305
90
|
>
|
|
306
|
-
<span>{{
|
|
307
|
-
<span
|
|
308
|
-
|
|
309
|
-
class="sr-only"
|
|
310
|
-
>
|
|
311
|
-
{{ menu.keywords.join(" ") }}
|
|
91
|
+
<span>{{ child.title }}</span>
|
|
92
|
+
<span class="sr-only">
|
|
93
|
+
{{ child.keywords?.join(" ") }}
|
|
312
94
|
</span>
|
|
313
95
|
</CommandItem>
|
|
314
96
|
</CommandGroup>
|
|
315
97
|
<CommandGroup v-else>
|
|
316
98
|
<CommandItem
|
|
317
|
-
:value="
|
|
318
|
-
|
|
99
|
+
:value="command.id"
|
|
100
|
+
:disabled="command.disabled"
|
|
101
|
+
@select="command.effect?.pipe(Effect.tap(() => {
|
|
102
|
+
ui.isCommandPaletteOpen = false;
|
|
103
|
+
})).pipe(Effect.runPromise)"
|
|
319
104
|
>
|
|
320
|
-
<span>{{
|
|
321
|
-
<span
|
|
322
|
-
|
|
323
|
-
class="sr-only"
|
|
324
|
-
>
|
|
325
|
-
{{ navigation.keywords.join(" ") }}
|
|
105
|
+
<span>{{ command.title }}</span>
|
|
106
|
+
<span class="sr-only">
|
|
107
|
+
{{ command.keywords?.join(" ") }}
|
|
326
108
|
</span>
|
|
327
109
|
</CommandItem>
|
|
328
110
|
</CommandGroup>
|
|
329
111
|
</template>
|
|
330
112
|
|
|
331
113
|
<template
|
|
332
|
-
v-for="
|
|
333
|
-
:key="
|
|
114
|
+
v-for="navigation in props.sidebar"
|
|
115
|
+
:key="navigation.id"
|
|
334
116
|
>
|
|
335
117
|
<CommandGroup
|
|
336
|
-
v-if="'children' in
|
|
337
|
-
:heading="
|
|
118
|
+
v-if="'children' in navigation"
|
|
119
|
+
:heading="navigation.title"
|
|
338
120
|
>
|
|
339
121
|
<CommandItem
|
|
340
|
-
v-for="
|
|
341
|
-
:key="
|
|
342
|
-
:value="
|
|
343
|
-
|
|
344
|
-
@select="executePaletteItem({ type: 'command', effect: child.effect })"
|
|
122
|
+
v-for="menu in navigation.children"
|
|
123
|
+
:key="menu.id"
|
|
124
|
+
:value="menu.id"
|
|
125
|
+
@select="$router.replace(menu.route)"
|
|
345
126
|
>
|
|
346
|
-
<span>{{
|
|
347
|
-
<span
|
|
348
|
-
v-if="child.keywords.length > 0"
|
|
349
|
-
class="sr-only"
|
|
350
|
-
>
|
|
351
|
-
{{ child.keywords.join(" ") }}
|
|
352
|
-
</span>
|
|
127
|
+
<span>{{ menu.title }}</span>
|
|
353
128
|
</CommandItem>
|
|
354
129
|
</CommandGroup>
|
|
355
130
|
<CommandGroup v-else>
|
|
356
131
|
<CommandItem
|
|
357
|
-
:value="
|
|
358
|
-
|
|
359
|
-
@select="executePaletteItem({ type: 'command', effect: command.effect })"
|
|
132
|
+
:value="navigation.id"
|
|
133
|
+
@select="$router.replace(navigation.route)"
|
|
360
134
|
>
|
|
361
|
-
<span>{{
|
|
362
|
-
<span
|
|
363
|
-
v-if="command.keywords.length > 0"
|
|
364
|
-
class="sr-only"
|
|
365
|
-
>
|
|
366
|
-
{{ command.keywords.join(" ") }}
|
|
367
|
-
</span>
|
|
135
|
+
<span>{{ navigation.title }}</span>
|
|
368
136
|
</CommandItem>
|
|
369
137
|
</CommandGroup>
|
|
370
138
|
</template>
|
|
@@ -393,7 +161,7 @@ watch(
|
|
|
393
161
|
@click.prevent="stopProfileCloseTimer();
|
|
394
162
|
ui.isProfileDropdownOpen = true"
|
|
395
163
|
>
|
|
396
|
-
|
|
164
|
+
<slot name="profile" />
|
|
397
165
|
</button>
|
|
398
166
|
</DropdownMenuTrigger>
|
|
399
167
|
<DropdownMenuContent
|
|
@@ -404,33 +172,37 @@ watch(
|
|
|
404
172
|
startProfileCloseTimer()"
|
|
405
173
|
>
|
|
406
174
|
<template
|
|
407
|
-
v-for="command in
|
|
175
|
+
v-for="command in props.commands"
|
|
408
176
|
:key="command.id"
|
|
409
177
|
>
|
|
410
|
-
<template v-if="'children' in command">
|
|
178
|
+
<template v-if="'children' in command && command.children.some((child2) => !child2.hidden)">
|
|
411
179
|
<DropdownMenuLabel>
|
|
412
180
|
{{ command.title }}
|
|
413
181
|
</DropdownMenuLabel>
|
|
414
182
|
<DropdownMenuGroup>
|
|
415
|
-
<
|
|
183
|
+
<template
|
|
416
184
|
v-for="child in command.children"
|
|
417
185
|
:key="child.id"
|
|
418
|
-
:disabled="child.disabled"
|
|
419
|
-
@select="executeProfileCommand(child.effect)"
|
|
420
186
|
>
|
|
421
|
-
<
|
|
422
|
-
v-if="child.
|
|
423
|
-
:
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
187
|
+
<DropdownMenuItem
|
|
188
|
+
v-if="!child.hidden"
|
|
189
|
+
:disabled="child.disabled"
|
|
190
|
+
@select="child.effect?.pipe(Effect.runPromise)"
|
|
191
|
+
>
|
|
192
|
+
<Icon
|
|
193
|
+
v-if="child.icon"
|
|
194
|
+
:icon="child.icon"
|
|
195
|
+
/>
|
|
196
|
+
{{ child.title }}
|
|
197
|
+
</DropdownMenuItem>
|
|
198
|
+
</template>
|
|
427
199
|
</DropdownMenuGroup>
|
|
428
200
|
<DropdownMenuSeparator />
|
|
429
201
|
</template>
|
|
430
202
|
<DropdownMenuItem
|
|
431
|
-
v-else
|
|
203
|
+
v-else-if="!('children' in command) && !command.hidden"
|
|
432
204
|
:disabled="command.disabled"
|
|
433
|
-
@select="
|
|
205
|
+
@select="command.effect?.pipe(Effect.runPromise)"
|
|
434
206
|
>
|
|
435
207
|
<Icon
|
|
436
208
|
v-if="command.icon"
|
|
@@ -439,7 +211,7 @@ watch(
|
|
|
439
211
|
{{ command.title }}
|
|
440
212
|
</DropdownMenuItem>
|
|
441
213
|
</template>
|
|
442
|
-
<DropdownMenuSeparator v-if="
|
|
214
|
+
<DropdownMenuSeparator v-if="props.commands?.some((command2) => 'children' in command2 ? command2.children.some((child2) => !child2.hidden) : !command2.hidden)" />
|
|
443
215
|
<DropdownMenuItem disabled>
|
|
444
216
|
<Icon icon="fluent:history-20-regular" />
|
|
445
217
|
{{ t("build") }}
|
|
@@ -496,11 +268,11 @@ watch(
|
|
|
496
268
|
<SidebarMenuButton
|
|
497
269
|
v-if="'route' in menu"
|
|
498
270
|
as-child
|
|
499
|
-
:is-active="
|
|
271
|
+
:is-active="active === menu.route"
|
|
500
272
|
>
|
|
501
273
|
<button
|
|
502
274
|
type="button"
|
|
503
|
-
@click="
|
|
275
|
+
@click="active = menu.route"
|
|
504
276
|
>
|
|
505
277
|
<Icon
|
|
506
278
|
v-if="menu.icon"
|
|
@@ -532,11 +304,11 @@ watch(
|
|
|
532
304
|
<SidebarMenuItem>
|
|
533
305
|
<SidebarMenuButton
|
|
534
306
|
as-child
|
|
535
|
-
:is-active="
|
|
307
|
+
:is-active="active === group.route"
|
|
536
308
|
>
|
|
537
309
|
<button
|
|
538
310
|
type="button"
|
|
539
|
-
@click="
|
|
311
|
+
@click="active = group.route"
|
|
540
312
|
>
|
|
541
313
|
<Icon
|
|
542
314
|
v-if="group.icon"
|
|
@@ -557,7 +329,7 @@ watch(
|
|
|
557
329
|
<main class="flex-1 flex flex-col bg-zinc-100 overflow-hidden">
|
|
558
330
|
<nav class="bg-white p-1 border-b border-zinc-100 h-10 flex items-center justify-start gap-1 overflow-x-auto min-w-0">
|
|
559
331
|
<div
|
|
560
|
-
v-for="tab in
|
|
332
|
+
v-for="tab in tabs"
|
|
561
333
|
:key="tab"
|
|
562
334
|
:class="[
|
|
563
335
|
'h-8 max-w-60 border rounded px-2 text-sm flex items-center gap-2 shrink-0 min-w-0 transition-colors',
|
|
@@ -567,14 +339,15 @@ watch(
|
|
|
567
339
|
<button
|
|
568
340
|
type="button"
|
|
569
341
|
class="truncate cursor-pointer"
|
|
570
|
-
@click="
|
|
342
|
+
@click="active = tab"
|
|
571
343
|
>
|
|
572
|
-
{{
|
|
344
|
+
{{ nameOf(tab) }}
|
|
573
345
|
</button>
|
|
574
346
|
<button
|
|
347
|
+
v-if="tabs.size !== 1"
|
|
575
348
|
type="button"
|
|
576
349
|
class="cursor-pointer text-zinc-500 hover:text-zinc-900"
|
|
577
|
-
@click.stop="
|
|
350
|
+
@click.stop="close(tab)"
|
|
578
351
|
>
|
|
579
352
|
<Icon icon="fluent:dismiss-20-regular" />
|
|
580
353
|
</button>
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type { Scope } from 'effect';
|
|
2
2
|
import { Effect } from 'effect';
|
|
3
|
+
import { type Sidebar as SidebarType } from '../composables/useNavigationTabs.js';
|
|
4
|
+
type ProfileCommandInputItem = Readonly<{
|
|
5
|
+
id: string | number;
|
|
6
|
+
title: string;
|
|
7
|
+
icon?: string;
|
|
8
|
+
hidden?: boolean;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
effect?: Effect.Effect<void>;
|
|
11
|
+
keywords?: Array<string>;
|
|
12
|
+
}>;
|
|
13
|
+
type ProfileCommandInputGroup = Readonly<{
|
|
14
|
+
id: string | number;
|
|
15
|
+
title: string;
|
|
16
|
+
icon?: string;
|
|
17
|
+
children: Array<ProfileCommandInputItem>;
|
|
18
|
+
}>;
|
|
3
19
|
type __VLS_Props = {
|
|
4
20
|
/**
|
|
5
21
|
* 应用的全局 DSL(CEL)执行上下文。传入一个 Effect,在应用启动时运行一次;
|
|
@@ -38,12 +54,26 @@ type __VLS_Props = {
|
|
|
38
54
|
* // CEL 中可访问 user.name、permissions.canEdit 等;失败时在 effect 内兜底,不抛错
|
|
39
55
|
*/
|
|
40
56
|
dsl?: Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
|
|
57
|
+
/**
|
|
58
|
+
* Sidebar config.
|
|
59
|
+
*
|
|
60
|
+
* `menus` is structured navigation data.
|
|
61
|
+
*/
|
|
62
|
+
sidebar?: SidebarType;
|
|
63
|
+
/**
|
|
64
|
+
* Profile command config.
|
|
65
|
+
*
|
|
66
|
+
* Structured command entries for profile menu and command palette.
|
|
67
|
+
*/
|
|
68
|
+
commands?: Array<ProfileCommandInputItem | ProfileCommandInputGroup>;
|
|
41
69
|
};
|
|
42
|
-
declare var
|
|
70
|
+
declare var __VLS_108: {}, __VLS_124: {}, __VLS_334: {};
|
|
43
71
|
type __VLS_Slots = {} & {
|
|
44
|
-
menu?: (props: typeof
|
|
72
|
+
menu?: (props: typeof __VLS_108) => any;
|
|
73
|
+
} & {
|
|
74
|
+
profile?: (props: typeof __VLS_124) => any;
|
|
45
75
|
} & {
|
|
46
|
-
default?: (props: typeof
|
|
76
|
+
default?: (props: typeof __VLS_334) => any;
|
|
47
77
|
};
|
|
48
78
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
49
79
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -7,7 +7,7 @@ type Group<K extends string | number> = Readonly<{
|
|
|
7
7
|
children: Array<Item<K>>;
|
|
8
8
|
}>;
|
|
9
9
|
type Favoritable<K extends string | number> = Item<K> | Group<K>;
|
|
10
|
-
export declare function useFavorite<K extends string | number, T extends Favoritable<K>>(key: string, items: MaybeRefOrGetter<
|
|
10
|
+
export declare function useFavorite<K extends string | number, T extends Favoritable<K>>(key: string, items: MaybeRefOrGetter<ReadonlyArray<T>>): {
|
|
11
11
|
favorite: <I extends Item<K>>(item: I, of: K) => void;
|
|
12
12
|
unfavorite: <I extends Item<K>>(item: I, of?: K) => void;
|
|
13
13
|
toggle: <I extends Item<K>>(item: I, of: K) => void;
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import { type MaybeRefOrGetter } from 'vue';
|
|
2
|
+
type SidebarMenuItem = Readonly<{
|
|
3
|
+
id: string | number;
|
|
4
|
+
title: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
route: string;
|
|
7
|
+
}>;
|
|
8
|
+
type SidebarMenuGroup = Readonly<{
|
|
9
|
+
id: string | number;
|
|
10
|
+
title: string;
|
|
11
|
+
icon?: string;
|
|
12
|
+
children: Array<SidebarMenuItem>;
|
|
13
|
+
}>;
|
|
14
|
+
export type Sidebar = ReadonlyArray<SidebarMenuItem | SidebarMenuGroup>;
|
|
15
|
+
export declare function useNavigationTabs(navigations: MaybeRefOrGetter<Sidebar>): {
|
|
2
16
|
tabs: import("@vueuse/shared").RemovableRef<Set<string>>;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
closeTab: (tab: string) => void;
|
|
17
|
+
nameOf: (path: string) => string | undefined;
|
|
18
|
+
close: (raw: string) => void;
|
|
19
|
+
active: import("vue").WritableComputedRef<string | undefined, string>;
|
|
7
20
|
};
|
|
21
|
+
export {};
|