@shwfed/nuxt 0.1.75 → 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 -311
- 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,25 +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({
|
|
31
|
+
title: () => active.value && nameOf(active.value),
|
|
34
32
|
bodyAttrs: {
|
|
35
33
|
style: {
|
|
36
34
|
"--primary": "#2DA8BC",
|
|
@@ -38,221 +36,18 @@ useHead({
|
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
});
|
|
41
|
-
const props = defineProps({
|
|
42
|
-
dsl: { type: null, required: false }
|
|
43
|
-
});
|
|
44
|
-
const ui = reactive({
|
|
45
|
-
isCommandPaletteOpen: false,
|
|
46
|
-
isProfileDropdownOpen: false
|
|
47
|
-
});
|
|
48
39
|
const { start: startProfileCloseTimer, stop: stopProfileCloseTimer } = useTimeoutFn(() => {
|
|
49
40
|
ui.isProfileDropdownOpen = false;
|
|
50
41
|
}, 150, { immediate: false });
|
|
51
42
|
const { meta_k } = useMagicKeys();
|
|
52
43
|
whenever(() => meta_k?.value, () => ui.isCommandPaletteOpen = !ui.isCommandPaletteOpen);
|
|
53
44
|
setGlobalDslContext(await props.dsl?.pipe(Effect.scoped).pipe(Effect.runPromise) ?? {});
|
|
54
|
-
const navigationItem = z.object({
|
|
55
|
-
id: z.union([z.string(), z.number()]),
|
|
56
|
-
title: z.string(),
|
|
57
|
-
icon: z.string().optional(),
|
|
58
|
-
route: z.string(),
|
|
59
|
-
keywords: z.array(z.string()).optional()
|
|
60
|
-
});
|
|
61
|
-
const navigationGroup = z.object({
|
|
62
|
-
id: z.union([z.string(), z.number()]),
|
|
63
|
-
title: z.string(),
|
|
64
|
-
icon: z.string().optional(),
|
|
65
|
-
children: z.array(navigationItem)
|
|
66
|
-
});
|
|
67
|
-
const navigationEntry = z.union([navigationGroup, navigationItem]);
|
|
68
|
-
const navigationItems = computed(() => {
|
|
69
|
-
try {
|
|
70
|
-
const result = $dsl.evaluate`${config.sidebar.menus}`();
|
|
71
|
-
return z.array(navigationEntry).parse(result);
|
|
72
|
-
} catch {
|
|
73
|
-
return [];
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
45
|
const {
|
|
77
46
|
isFavorited: isNavigationFavorited,
|
|
78
47
|
canBeFavorited: canBeNavigationFavorited,
|
|
79
48
|
toggle: toggleNavigationFavorite,
|
|
80
49
|
withFavorites: navigations
|
|
81
|
-
} = useFavorite("navigation",
|
|
82
|
-
const isNavigationItemActive = (itemRoute) => isRouteActive(route.path, itemRoute);
|
|
83
|
-
const getTabLabel = (tab) => {
|
|
84
|
-
return resolveNavigationTitle(navigationItems.value, tab);
|
|
85
|
-
};
|
|
86
|
-
const activeTabLabel = computed(() => {
|
|
87
|
-
if (active.value === void 0)
|
|
88
|
-
return void 0;
|
|
89
|
-
return getTabLabel(active.value);
|
|
90
|
-
});
|
|
91
|
-
useHead(() => ({
|
|
92
|
-
title: activeTabLabel.value
|
|
93
|
-
}));
|
|
94
|
-
const profileName = computed(() => {
|
|
95
|
-
if (config.profile.name === void 0)
|
|
96
|
-
return t("profile");
|
|
97
|
-
try {
|
|
98
|
-
const content = $md.inline`${config.profile.name}`();
|
|
99
|
-
const plainText = content.replace(/<[^>]*>/g, "").trim();
|
|
100
|
-
if (plainText.length === 0)
|
|
101
|
-
return t("profile");
|
|
102
|
-
return plainText;
|
|
103
|
-
} catch {
|
|
104
|
-
return t("profile");
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
const commandItem = z.object({
|
|
108
|
-
id: z.union([z.string(), z.number()]),
|
|
109
|
-
title: z.string(),
|
|
110
|
-
icon: z.string().optional(),
|
|
111
|
-
hidden: z.string().optional(),
|
|
112
|
-
disabled: z.string().optional(),
|
|
113
|
-
effect: z.string().optional(),
|
|
114
|
-
keywords: z.array(z.string()).optional()
|
|
115
|
-
});
|
|
116
|
-
const commandGroup = z.object({
|
|
117
|
-
id: z.union([z.string(), z.number()]),
|
|
118
|
-
title: z.string(),
|
|
119
|
-
icon: z.string().optional(),
|
|
120
|
-
children: z.array(commandItem)
|
|
121
|
-
});
|
|
122
|
-
const evaluateDslBoolean = (expression) => {
|
|
123
|
-
if (expression === void 0)
|
|
124
|
-
return false;
|
|
125
|
-
try {
|
|
126
|
-
return $dsl.evaluate`${expression}`() === true;
|
|
127
|
-
} catch {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
const normalizeEffectExpression = (expression) => {
|
|
132
|
-
return expression.replace(/([{,]\s*)([a-z_][\w-]*)(\s*:)/gi, "$1'$2'$3");
|
|
133
|
-
};
|
|
134
|
-
const profileCommands = computed(() => {
|
|
135
|
-
if (config.commands === void 0)
|
|
136
|
-
return [];
|
|
137
|
-
try {
|
|
138
|
-
const result = $dsl.evaluate`${config.commands}`();
|
|
139
|
-
const entries = z.array(z.union([commandGroup, commandItem])).parse(result);
|
|
140
|
-
const normalized = [];
|
|
141
|
-
for (const entry of entries) {
|
|
142
|
-
if ("children" in entry) {
|
|
143
|
-
const children = entry.children.filter((child) => !evaluateDslBoolean(child.hidden)).map((child) => ({
|
|
144
|
-
id: child.id,
|
|
145
|
-
title: child.title,
|
|
146
|
-
icon: child.icon,
|
|
147
|
-
disabled: evaluateDslBoolean(child.disabled),
|
|
148
|
-
effect: child.effect,
|
|
149
|
-
keywords: child.keywords
|
|
150
|
-
}));
|
|
151
|
-
if (children.length > 0) {
|
|
152
|
-
normalized.push({
|
|
153
|
-
id: entry.id,
|
|
154
|
-
title: entry.title,
|
|
155
|
-
icon: entry.icon,
|
|
156
|
-
children
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
if (evaluateDslBoolean(entry.hidden))
|
|
162
|
-
continue;
|
|
163
|
-
normalized.push({
|
|
164
|
-
id: entry.id,
|
|
165
|
-
title: entry.title,
|
|
166
|
-
icon: entry.icon,
|
|
167
|
-
disabled: evaluateDslBoolean(entry.disabled),
|
|
168
|
-
effect: entry.effect,
|
|
169
|
-
keywords: entry.keywords
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
return normalized;
|
|
173
|
-
} catch {
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
const isBodyInit = (value) => {
|
|
178
|
-
return typeof value === "string" || value instanceof Blob || value instanceof ArrayBuffer || ArrayBuffer.isView(value) || value instanceof FormData || value instanceof URLSearchParams || value instanceof ReadableStream;
|
|
179
|
-
};
|
|
180
|
-
const toApiRequestBody = (value) => {
|
|
181
|
-
if (value === void 0 || value === null)
|
|
182
|
-
return value;
|
|
183
|
-
if (isBodyInit(value))
|
|
184
|
-
return value;
|
|
185
|
-
if (typeof value === "object")
|
|
186
|
-
return value;
|
|
187
|
-
return void 0;
|
|
188
|
-
};
|
|
189
|
-
const executeCommandEffect = async (effect) => {
|
|
190
|
-
if (effect === void 0)
|
|
191
|
-
return false;
|
|
192
|
-
let action;
|
|
193
|
-
try {
|
|
194
|
-
let result;
|
|
195
|
-
try {
|
|
196
|
-
result = $dsl.evaluate`${effect}`();
|
|
197
|
-
} catch {
|
|
198
|
-
result = $dsl.evaluate`${normalizeEffectExpression(effect)}`();
|
|
199
|
-
}
|
|
200
|
-
action = commandActionSchema.parse(result);
|
|
201
|
-
await executeCommandAction(action, {
|
|
202
|
-
logout: $logout,
|
|
203
|
-
request: async (requestAction) => $api(requestAction.url, {
|
|
204
|
-
method: requestAction.method,
|
|
205
|
-
body: toApiRequestBody(requestAction.body),
|
|
206
|
-
query: requestAction.query,
|
|
207
|
-
headers: requestAction.headers
|
|
208
|
-
})
|
|
209
|
-
});
|
|
210
|
-
return true;
|
|
211
|
-
} catch (error) {
|
|
212
|
-
if (action?.type === "request") {
|
|
213
|
-
const message = error instanceof Error && error.message.length > 0 ? error.message : "Request failed";
|
|
214
|
-
$toast.error(message);
|
|
215
|
-
}
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
const executeProfileCommand = async (effect) => {
|
|
220
|
-
if (effect === void 0 || await executeCommandEffect(effect))
|
|
221
|
-
ui.isProfileDropdownOpen = false;
|
|
222
|
-
};
|
|
223
|
-
const normalizedNavigationForPalette = computed(() => {
|
|
224
|
-
try {
|
|
225
|
-
return z.array(navigationEntry).parse(navigations.value);
|
|
226
|
-
} catch {
|
|
227
|
-
return navigationItems.value;
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
const paletteNavigation = computed(() => splitNavigationForPalette(normalizedNavigationForPalette.value));
|
|
231
|
-
const favoriteRoutesForPalette = computed(() => paletteNavigation.value.favoriteRoutes);
|
|
232
|
-
const navigationForPalette = computed(() => paletteNavigation.value.navigationEntries);
|
|
233
|
-
const commandForPalette = computed(() => normalizeCommandsForPalette(profileCommands.value));
|
|
234
|
-
const executePaletteItem = async (item) => {
|
|
235
|
-
if (item.type === "route") {
|
|
236
|
-
activateTab(item.route);
|
|
237
|
-
ui.isCommandPaletteOpen = false;
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
if (item.effect !== void 0 && !await executeCommandEffect(item.effect))
|
|
241
|
-
return;
|
|
242
|
-
ui.isCommandPaletteOpen = false;
|
|
243
|
-
};
|
|
244
|
-
watch(
|
|
245
|
-
() => route.path,
|
|
246
|
-
(path) => {
|
|
247
|
-
applyRootNavigationFallback({
|
|
248
|
-
path,
|
|
249
|
-
items: navigationItems.value,
|
|
250
|
-
tabs: tabs.value,
|
|
251
|
-
activateTab
|
|
252
|
-
});
|
|
253
|
-
},
|
|
254
|
-
{ immediate: true }
|
|
255
|
-
);
|
|
50
|
+
} = useFavorite("navigation", () => props.sidebar ?? []);
|
|
256
51
|
</script>
|
|
257
52
|
|
|
258
53
|
<template>
|
|
@@ -276,102 +71,68 @@ watch(
|
|
|
276
71
|
</section>
|
|
277
72
|
</CommandEmpty>
|
|
278
73
|
|
|
279
|
-
<CommandGroup
|
|
280
|
-
v-if="favoriteRoutesForPalette.length > 0"
|
|
281
|
-
:heading="t('favorites')"
|
|
282
|
-
>
|
|
283
|
-
<CommandItem
|
|
284
|
-
v-for="menu in favoriteRoutesForPalette"
|
|
285
|
-
:key="`fav:${menu.id}:${menu.route}`"
|
|
286
|
-
:value="createPaletteItemValue('fav', `${menu.id}:${menu.route}`)"
|
|
287
|
-
@select="executePaletteItem({ type: 'route', route: menu.route })"
|
|
288
|
-
>
|
|
289
|
-
<span>{{ menu.title }}</span>
|
|
290
|
-
<span
|
|
291
|
-
v-if="menu.keywords.length > 0"
|
|
292
|
-
class="sr-only"
|
|
293
|
-
>
|
|
294
|
-
{{ menu.keywords.join(" ") }}
|
|
295
|
-
</span>
|
|
296
|
-
</CommandItem>
|
|
297
|
-
</CommandGroup>
|
|
298
|
-
|
|
299
74
|
<template
|
|
300
|
-
v-for="
|
|
301
|
-
:key="
|
|
75
|
+
v-for="command in props.commands"
|
|
76
|
+
:key="command.id"
|
|
302
77
|
>
|
|
303
78
|
<CommandGroup
|
|
304
|
-
v-if="'children' in
|
|
305
|
-
:heading="
|
|
79
|
+
v-if="'children' in command"
|
|
80
|
+
:heading="command.title"
|
|
306
81
|
>
|
|
307
82
|
<CommandItem
|
|
308
|
-
v-for="
|
|
309
|
-
:key="
|
|
310
|
-
:value="
|
|
311
|
-
|
|
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)"
|
|
312
90
|
>
|
|
313
|
-
<span>{{
|
|
314
|
-
<span
|
|
315
|
-
|
|
316
|
-
class="sr-only"
|
|
317
|
-
>
|
|
318
|
-
{{ menu.keywords.join(" ") }}
|
|
91
|
+
<span>{{ child.title }}</span>
|
|
92
|
+
<span class="sr-only">
|
|
93
|
+
{{ child.keywords?.join(" ") }}
|
|
319
94
|
</span>
|
|
320
95
|
</CommandItem>
|
|
321
96
|
</CommandGroup>
|
|
322
97
|
<CommandGroup v-else>
|
|
323
98
|
<CommandItem
|
|
324
|
-
:value="
|
|
325
|
-
|
|
99
|
+
:value="command.id"
|
|
100
|
+
:disabled="command.disabled"
|
|
101
|
+
@select="command.effect?.pipe(Effect.tap(() => {
|
|
102
|
+
ui.isCommandPaletteOpen = false;
|
|
103
|
+
})).pipe(Effect.runPromise)"
|
|
326
104
|
>
|
|
327
|
-
<span>{{
|
|
328
|
-
<span
|
|
329
|
-
|
|
330
|
-
class="sr-only"
|
|
331
|
-
>
|
|
332
|
-
{{ navigation.keywords.join(" ") }}
|
|
105
|
+
<span>{{ command.title }}</span>
|
|
106
|
+
<span class="sr-only">
|
|
107
|
+
{{ command.keywords?.join(" ") }}
|
|
333
108
|
</span>
|
|
334
109
|
</CommandItem>
|
|
335
110
|
</CommandGroup>
|
|
336
111
|
</template>
|
|
337
112
|
|
|
338
113
|
<template
|
|
339
|
-
v-for="
|
|
340
|
-
:key="
|
|
114
|
+
v-for="navigation in props.sidebar"
|
|
115
|
+
:key="navigation.id"
|
|
341
116
|
>
|
|
342
117
|
<CommandGroup
|
|
343
|
-
v-if="'children' in
|
|
344
|
-
:heading="
|
|
118
|
+
v-if="'children' in navigation"
|
|
119
|
+
:heading="navigation.title"
|
|
345
120
|
>
|
|
346
121
|
<CommandItem
|
|
347
|
-
v-for="
|
|
348
|
-
:key="
|
|
349
|
-
:value="
|
|
350
|
-
|
|
351
|
-
@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)"
|
|
352
126
|
>
|
|
353
|
-
<span>{{
|
|
354
|
-
<span
|
|
355
|
-
v-if="child.keywords.length > 0"
|
|
356
|
-
class="sr-only"
|
|
357
|
-
>
|
|
358
|
-
{{ child.keywords.join(" ") }}
|
|
359
|
-
</span>
|
|
127
|
+
<span>{{ menu.title }}</span>
|
|
360
128
|
</CommandItem>
|
|
361
129
|
</CommandGroup>
|
|
362
130
|
<CommandGroup v-else>
|
|
363
131
|
<CommandItem
|
|
364
|
-
:value="
|
|
365
|
-
|
|
366
|
-
@select="executePaletteItem({ type: 'command', effect: command.effect })"
|
|
132
|
+
:value="navigation.id"
|
|
133
|
+
@select="$router.replace(navigation.route)"
|
|
367
134
|
>
|
|
368
|
-
<span>{{
|
|
369
|
-
<span
|
|
370
|
-
v-if="command.keywords.length > 0"
|
|
371
|
-
class="sr-only"
|
|
372
|
-
>
|
|
373
|
-
{{ command.keywords.join(" ") }}
|
|
374
|
-
</span>
|
|
135
|
+
<span>{{ navigation.title }}</span>
|
|
375
136
|
</CommandItem>
|
|
376
137
|
</CommandGroup>
|
|
377
138
|
</template>
|
|
@@ -400,7 +161,7 @@ watch(
|
|
|
400
161
|
@click.prevent="stopProfileCloseTimer();
|
|
401
162
|
ui.isProfileDropdownOpen = true"
|
|
402
163
|
>
|
|
403
|
-
|
|
164
|
+
<slot name="profile" />
|
|
404
165
|
</button>
|
|
405
166
|
</DropdownMenuTrigger>
|
|
406
167
|
<DropdownMenuContent
|
|
@@ -411,33 +172,37 @@ watch(
|
|
|
411
172
|
startProfileCloseTimer()"
|
|
412
173
|
>
|
|
413
174
|
<template
|
|
414
|
-
v-for="command in
|
|
175
|
+
v-for="command in props.commands"
|
|
415
176
|
:key="command.id"
|
|
416
177
|
>
|
|
417
|
-
<template v-if="'children' in command">
|
|
178
|
+
<template v-if="'children' in command && command.children.some((child2) => !child2.hidden)">
|
|
418
179
|
<DropdownMenuLabel>
|
|
419
180
|
{{ command.title }}
|
|
420
181
|
</DropdownMenuLabel>
|
|
421
182
|
<DropdownMenuGroup>
|
|
422
|
-
<
|
|
183
|
+
<template
|
|
423
184
|
v-for="child in command.children"
|
|
424
185
|
:key="child.id"
|
|
425
|
-
:disabled="child.disabled"
|
|
426
|
-
@select="executeProfileCommand(child.effect)"
|
|
427
186
|
>
|
|
428
|
-
<
|
|
429
|
-
v-if="child.
|
|
430
|
-
:
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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>
|
|
434
199
|
</DropdownMenuGroup>
|
|
435
200
|
<DropdownMenuSeparator />
|
|
436
201
|
</template>
|
|
437
202
|
<DropdownMenuItem
|
|
438
|
-
v-else
|
|
203
|
+
v-else-if="!('children' in command) && !command.hidden"
|
|
439
204
|
:disabled="command.disabled"
|
|
440
|
-
@select="
|
|
205
|
+
@select="command.effect?.pipe(Effect.runPromise)"
|
|
441
206
|
>
|
|
442
207
|
<Icon
|
|
443
208
|
v-if="command.icon"
|
|
@@ -446,7 +211,7 @@ watch(
|
|
|
446
211
|
{{ command.title }}
|
|
447
212
|
</DropdownMenuItem>
|
|
448
213
|
</template>
|
|
449
|
-
<DropdownMenuSeparator v-if="
|
|
214
|
+
<DropdownMenuSeparator v-if="props.commands?.some((command2) => 'children' in command2 ? command2.children.some((child2) => !child2.hidden) : !command2.hidden)" />
|
|
450
215
|
<DropdownMenuItem disabled>
|
|
451
216
|
<Icon icon="fluent:history-20-regular" />
|
|
452
217
|
{{ t("build") }}
|
|
@@ -503,11 +268,11 @@ watch(
|
|
|
503
268
|
<SidebarMenuButton
|
|
504
269
|
v-if="'route' in menu"
|
|
505
270
|
as-child
|
|
506
|
-
:is-active="
|
|
271
|
+
:is-active="active === menu.route"
|
|
507
272
|
>
|
|
508
273
|
<button
|
|
509
274
|
type="button"
|
|
510
|
-
@click="
|
|
275
|
+
@click="active = menu.route"
|
|
511
276
|
>
|
|
512
277
|
<Icon
|
|
513
278
|
v-if="menu.icon"
|
|
@@ -539,11 +304,11 @@ watch(
|
|
|
539
304
|
<SidebarMenuItem>
|
|
540
305
|
<SidebarMenuButton
|
|
541
306
|
as-child
|
|
542
|
-
:is-active="
|
|
307
|
+
:is-active="active === group.route"
|
|
543
308
|
>
|
|
544
309
|
<button
|
|
545
310
|
type="button"
|
|
546
|
-
@click="
|
|
311
|
+
@click="active = group.route"
|
|
547
312
|
>
|
|
548
313
|
<Icon
|
|
549
314
|
v-if="group.icon"
|
|
@@ -564,7 +329,7 @@ watch(
|
|
|
564
329
|
<main class="flex-1 flex flex-col bg-zinc-100 overflow-hidden">
|
|
565
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">
|
|
566
331
|
<div
|
|
567
|
-
v-for="tab in
|
|
332
|
+
v-for="tab in tabs"
|
|
568
333
|
:key="tab"
|
|
569
334
|
:class="[
|
|
570
335
|
'h-8 max-w-60 border rounded px-2 text-sm flex items-center gap-2 shrink-0 min-w-0 transition-colors',
|
|
@@ -574,14 +339,15 @@ watch(
|
|
|
574
339
|
<button
|
|
575
340
|
type="button"
|
|
576
341
|
class="truncate cursor-pointer"
|
|
577
|
-
@click="
|
|
342
|
+
@click="active = tab"
|
|
578
343
|
>
|
|
579
|
-
{{
|
|
344
|
+
{{ nameOf(tab) }}
|
|
580
345
|
</button>
|
|
581
346
|
<button
|
|
347
|
+
v-if="tabs.size !== 1"
|
|
582
348
|
type="button"
|
|
583
349
|
class="cursor-pointer text-zinc-500 hover:text-zinc-900"
|
|
584
|
-
@click.stop="
|
|
350
|
+
@click.stop="close(tab)"
|
|
585
351
|
>
|
|
586
352
|
<Icon icon="fluent:dismiss-20-regular" />
|
|
587
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 {};
|