@shwfed/nuxt 0.10.10 → 0.10.12

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.
@@ -0,0 +1,275 @@
1
+ <script setup>
2
+ import { useNuxtApp, useRoute, useRouter } from "#app";
3
+ import { useCheating } from "#imports";
4
+ import { Icon } from "@iconify/vue";
5
+ import { computedAsync } from "@vueuse/core";
6
+ import { Effect } from "effect";
7
+ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from "vue";
8
+ import { useI18n } from "vue-i18n";
9
+ import { getLocalizedText } from "../../../utils/coders";
10
+ import { Button } from "../button";
11
+ import MenuTabsConfiguratorDialog from "../menu-tabs-configurator/MenuTabsConfiguratorDialog.vue";
12
+ import { Skeleton } from "../skeleton";
13
+ import { MenuTabsConfigC, normalizeMenuTabsConfigInput } from "./schema";
14
+ defineOptions({
15
+ inheritAttrs: false
16
+ });
17
+ const defaultConfig = {
18
+ menus: []
19
+ };
20
+ const props = defineProps({
21
+ config: { type: null, required: true },
22
+ context: { type: Object, required: false }
23
+ });
24
+ const emit = defineEmits(["update:config"]);
25
+ const { $dsl } = useNuxtApp();
26
+ const route = useRoute();
27
+ const router = useRouter();
28
+ const { locale, t } = useI18n();
29
+ const isCheating = useCheating();
30
+ const resolvedConfig = computedAsync(
31
+ async () => MenuTabsConfigC.parse(normalizeMenuTabsConfigInput(await props.config.pipe(Effect.runPromise)))
32
+ );
33
+ const displayConfig = ref(defaultConfig);
34
+ const isConfiguratorOpen = ref(false);
35
+ const listRef = ref(null);
36
+ const buttonElements = /* @__PURE__ */ new Map();
37
+ const indicatorMetrics = ref();
38
+ let resizeObserver;
39
+ let observedElements = /* @__PURE__ */ new Set();
40
+ let frameId = 0;
41
+ function cleanPath(path) {
42
+ const normalizedPath = path.trim().replace(/\/+$/g, "");
43
+ return normalizedPath.length > 0 ? normalizedPath : "/";
44
+ }
45
+ function getEvaluationContext() {
46
+ return {
47
+ ...props.context ?? {},
48
+ context: props.context ?? {}
49
+ };
50
+ }
51
+ const resolvedMenus = computed(
52
+ () => displayConfig.value.menus.map((menu) => {
53
+ let path;
54
+ try {
55
+ const result = $dsl.evaluate`${menu.to}`(getEvaluationContext());
56
+ if (typeof result === "string" && result.trim().length > 0) {
57
+ path = cleanPath(result);
58
+ }
59
+ } catch {
60
+ path = void 0;
61
+ }
62
+ return {
63
+ id: menu.id,
64
+ path,
65
+ title: getLocalizedText(menu.title, locale.value) ?? t("untitled-menu")
66
+ };
67
+ })
68
+ );
69
+ const activeMenuId = computed(() => {
70
+ const currentPath = cleanPath(route.path);
71
+ return resolvedMenus.value.find((menu) => menu.path === currentPath)?.id;
72
+ });
73
+ const indicatorStyle = computed(() => {
74
+ if (!indicatorMetrics.value) {
75
+ return {
76
+ opacity: 0,
77
+ transform: "translateX(0px)",
78
+ width: "0px"
79
+ };
80
+ }
81
+ return {
82
+ opacity: 1,
83
+ transform: `translateX(${indicatorMetrics.value.left}px)`,
84
+ width: `${indicatorMetrics.value.width}px`
85
+ };
86
+ });
87
+ function setMenuButtonRef(id, value) {
88
+ if (value instanceof HTMLButtonElement) {
89
+ if (buttonElements.get(id) === value) {
90
+ return;
91
+ }
92
+ buttonElements.set(id, value);
93
+ } else {
94
+ if (!buttonElements.has(id)) {
95
+ return;
96
+ }
97
+ buttonElements.delete(id);
98
+ }
99
+ syncObservedElements();
100
+ void scheduleIndicator();
101
+ }
102
+ function updateIndicator() {
103
+ const activeId = activeMenuId.value;
104
+ const activeButton = activeId ? buttonElements.get(activeId) : void 0;
105
+ if (!activeButton) {
106
+ indicatorMetrics.value = void 0;
107
+ return;
108
+ }
109
+ indicatorMetrics.value = {
110
+ left: activeButton.offsetLeft,
111
+ width: activeButton.offsetWidth
112
+ };
113
+ }
114
+ async function scheduleIndicator() {
115
+ if (typeof window !== "undefined") {
116
+ cancelAnimationFrame(frameId);
117
+ frameId = window.requestAnimationFrame(() => {
118
+ updateIndicator();
119
+ });
120
+ return;
121
+ }
122
+ await nextTick();
123
+ updateIndicator();
124
+ }
125
+ function syncObservedElements() {
126
+ if (!resizeObserver) {
127
+ return;
128
+ }
129
+ const nextObservedElements = /* @__PURE__ */ new Set();
130
+ if (listRef.value) {
131
+ nextObservedElements.add(listRef.value);
132
+ }
133
+ for (const button of buttonElements.values()) {
134
+ nextObservedElements.add(button);
135
+ }
136
+ for (const element of observedElements) {
137
+ if (!nextObservedElements.has(element)) {
138
+ resizeObserver.unobserve(element);
139
+ }
140
+ }
141
+ for (const element of nextObservedElements) {
142
+ if (!observedElements.has(element)) {
143
+ resizeObserver.observe(element);
144
+ }
145
+ }
146
+ observedElements = nextObservedElements;
147
+ }
148
+ function handleConfiguratorConfirm(nextConfig) {
149
+ displayConfig.value = nextConfig;
150
+ emit("update:config", nextConfig);
151
+ }
152
+ function handleMenuClick(menu) {
153
+ if (!menu.path) {
154
+ return;
155
+ }
156
+ void router.replace(menu.path);
157
+ }
158
+ watch(resolvedConfig, (value) => {
159
+ if (!value) {
160
+ return;
161
+ }
162
+ displayConfig.value = value;
163
+ }, { immediate: true });
164
+ watch(() => `${route.path}|${resolvedMenus.value.map((menu) => `${menu.id}:${menu.path ?? ""}`).join("|")}`, () => {
165
+ void scheduleIndicator();
166
+ }, { immediate: true });
167
+ onMounted(() => {
168
+ if (typeof ResizeObserver !== "undefined") {
169
+ resizeObserver = new ResizeObserver(() => {
170
+ void scheduleIndicator();
171
+ });
172
+ syncObservedElements();
173
+ }
174
+ void scheduleIndicator();
175
+ });
176
+ onUnmounted(() => {
177
+ cancelAnimationFrame(frameId);
178
+ resizeObserver?.disconnect();
179
+ observedElements = /* @__PURE__ */ new Set();
180
+ });
181
+ </script>
182
+
183
+ <script>
184
+ export { MenuTabsConfigC, MenuTabsConfigInputC, MenuTabsItemC, MenuTabsItemToC } from "./schema";
185
+ </script>
186
+
187
+ <template>
188
+ <div
189
+ v-bind="$attrs"
190
+ data-slot="menu-tabs-root"
191
+ class="relative"
192
+ >
193
+ <Button
194
+ v-if="isCheating"
195
+ data-slot="menu-tabs-configurator-trigger"
196
+ variant="ghost"
197
+ size="sm"
198
+ type="button"
199
+ class="absolute right-2 top-2 z-20 bg-white/90 shadow-xs backdrop-blur-sm hover:bg-white"
200
+ :aria-label="t('menu-tabs-open-configurator')"
201
+ :title="t('menu-tabs-open-configurator')"
202
+ @click="isConfiguratorOpen = true"
203
+ >
204
+ <Icon icon="fluent:settings-20-regular" />
205
+ </Button>
206
+
207
+ <MenuTabsConfiguratorDialog
208
+ v-if="resolvedConfig !== void 0"
209
+ v-model:open="isConfiguratorOpen"
210
+ :config="displayConfig"
211
+ @confirm="handleConfiguratorConfirm"
212
+ />
213
+
214
+ <Skeleton
215
+ v-if="resolvedConfig === void 0"
216
+ data-slot="menu-tabs-skeleton"
217
+ class="absolute inset-0 z-10 h-full w-full"
218
+ />
219
+
220
+ <div
221
+ data-slot="menu-tabs-container"
222
+ class="overflow-x-auto"
223
+ >
224
+ <div
225
+ ref="listRef"
226
+ data-slot="menu-tabs-list"
227
+ class="relative flex min-w-full w-max items-stretch gap-6 border-b-2 border-zinc-200 bg-white"
228
+ >
229
+ <button
230
+ v-for="menu in resolvedMenus"
231
+ :ref="(value) => setMenuButtonRef(menu.id, value)"
232
+ :key="menu.id"
233
+ type="button"
234
+ data-slot="menu-tabs-item"
235
+ :data-menu-id="menu.id"
236
+ :data-active="activeMenuId === menu.id ? 'true' : void 0"
237
+ :data-navigable="menu.path ? 'true' : 'false'"
238
+ :aria-current="activeMenuId === menu.id ? 'page' : void 0"
239
+ :aria-disabled="menu.path ? void 0 : 'true'"
240
+ :class="[
241
+ 'text-sm relative shrink-0 border-b-2 border-transparent py-2 font-semibold transition-colors duration-180',
242
+ activeMenuId === menu.id ? 'text-(--primary)' : 'text-zinc-700',
243
+ menu.path ? 'cursor-pointer hover:text-(--primary)' : 'cursor-default opacity-60'
244
+ ]"
245
+ @click="handleMenuClick(menu)"
246
+ >
247
+ {{ menu.title }}
248
+ </button>
249
+
250
+ <div
251
+ data-slot="menu-tabs-indicator"
252
+ class="pointer-events-none absolute -bottom-0.5 left-0 h-0.5 rounded bg-(--primary) transition-[transform,width,opacity] duration-200 ease-out"
253
+ :style="indicatorStyle"
254
+ />
255
+ </div>
256
+ </div>
257
+ </div>
258
+ </template>
259
+
260
+ <i18n lang="json">
261
+ {
262
+ "zh": {
263
+ "menu-tabs-open-configurator": "打开菜单标签配置",
264
+ "untitled-menu": "未命名菜单"
265
+ },
266
+ "ja": {
267
+ "menu-tabs-open-configurator": "メニュータブ設定を開く",
268
+ "untitled-menu": "名称未設定メニュー"
269
+ },
270
+ "en": {
271
+ "menu-tabs-open-configurator": "Open menu tabs configurator",
272
+ "untitled-menu": "Untitled menu"
273
+ }
274
+ }
275
+ </i18n>
@@ -0,0 +1,29 @@
1
+ import { Effect } from 'effect';
2
+ import { type MenuTabsConfigInput } from './schema.js';
3
+ export { MenuTabsConfigC, MenuTabsConfigInputC, MenuTabsItemC, MenuTabsItemToC } from './schema.js';
4
+ export type { MenuTabsConfig, MenuTabsConfigInput, MenuTabsItem } from './schema.js';
5
+ declare const _default: typeof __VLS_export;
6
+ export default _default;
7
+ declare const __VLS_export: import("vue").DefineComponent<{
8
+ config: Effect.Effect<MenuTabsConfigInput | undefined>;
9
+ context?: Record<string, unknown>;
10
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ "update:config": (args_0: Readonly<{
12
+ menus: readonly Readonly<{
13
+ id: string;
14
+ title: import("../../../utils/coders.js").LocaleValue;
15
+ to: string;
16
+ }>[];
17
+ }>) => any;
18
+ }, string, import("vue").PublicProps, Readonly<{
19
+ config: Effect.Effect<MenuTabsConfigInput | undefined>;
20
+ context?: Record<string, unknown>;
21
+ }> & Readonly<{
22
+ "onUpdate:config"?: ((args_0: Readonly<{
23
+ menus: readonly Readonly<{
24
+ id: string;
25
+ title: import("../../../utils/coders.js").LocaleValue;
26
+ to: string;
27
+ }>[];
28
+ }>) => any) | undefined;
29
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -0,0 +1,58 @@
1
+ import z from 'zod';
2
+ import type { LocaleValue } from '../../../utils/coders.js';
3
+ export declare const MenuTabsItemToC: z.ZodString;
4
+ export declare const MenuTabsItemC: z.ZodReadonly<z.ZodObject<{
5
+ id: z.ZodUUID;
6
+ title: z.ZodReadonly<z.ZodArray<z.ZodObject<{
7
+ locale: z.ZodEnum<{
8
+ zh: "zh";
9
+ ja: "ja";
10
+ en: "en";
11
+ ko: "ko";
12
+ }>;
13
+ message: z.ZodString;
14
+ }, z.core.$strip>>>;
15
+ to: z.ZodString;
16
+ }, z.core.$strict>>;
17
+ export declare const MenuTabsConfigC: z.ZodReadonly<z.ZodObject<{
18
+ menus: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
19
+ id: z.ZodUUID;
20
+ title: z.ZodReadonly<z.ZodArray<z.ZodObject<{
21
+ locale: z.ZodEnum<{
22
+ zh: "zh";
23
+ ja: "ja";
24
+ en: "en";
25
+ ko: "ko";
26
+ }>;
27
+ message: z.ZodString;
28
+ }, z.core.$strip>>>;
29
+ to: z.ZodString;
30
+ }, z.core.$strict>>>>;
31
+ }, z.core.$strict>>;
32
+ export declare const MenuTabsConfigInputC: z.ZodReadonly<z.ZodObject<{
33
+ menus: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
34
+ id: z.ZodUUID;
35
+ title: z.ZodReadonly<z.ZodArray<z.ZodObject<{
36
+ locale: z.ZodEnum<{
37
+ zh: "zh";
38
+ ja: "ja";
39
+ en: "en";
40
+ ko: "ko";
41
+ }>;
42
+ message: z.ZodString;
43
+ }, z.core.$strip>>>;
44
+ to: z.ZodString;
45
+ }, z.core.$strict>>>>;
46
+ }, z.core.$strict>>;
47
+ export type MenuTabsItem = Readonly<{
48
+ id: string;
49
+ title: LocaleValue;
50
+ to: string;
51
+ }>;
52
+ export type MenuTabsConfig = Readonly<{
53
+ menus: ReadonlyArray<MenuTabsItem>;
54
+ }>;
55
+ export type MenuTabsConfigInput = Readonly<{
56
+ menus?: ReadonlyArray<MenuTabsItem>;
57
+ }>;
58
+ export declare function normalizeMenuTabsConfigInput(value: unknown): unknown;
@@ -0,0 +1,24 @@
1
+ import z from "zod";
2
+ import { expressionC, localeC } from "../../../utils/coders.js";
3
+ const menuTabsItemIdC = z.uuid().describe("\u83DC\u5355\u9879\u552F\u4E00\u6807\u8BC6\uFF0C\u5FC5\u987B\u662F UUID");
4
+ export const MenuTabsItemToC = expressionC("string", { context: "dyn" }).describe("\u8FD4\u56DE\u76EE\u6807\u8DEF\u5F84\u7684 CEL \u8868\u8FBE\u5F0F\u3002\u53EF\u7528\u53D8\u91CF\uFF1Acontext\u3002\u5FC5\u987B\u8FD4\u56DE string\u3002");
5
+ export const MenuTabsItemC = z.strictObject({
6
+ id: menuTabsItemIdC,
7
+ title: localeC.describe("\u83DC\u5355\u540D\u79F0\u7684\u672C\u5730\u5316\u663E\u793A\u6587\u672C"),
8
+ to: MenuTabsItemToC
9
+ }).readonly();
10
+ export const MenuTabsConfigC = z.strictObject({
11
+ menus: z.array(MenuTabsItemC).readonly().describe("\u9876\u90E8\u83DC\u5355\u5217\u8868")
12
+ }).readonly();
13
+ export const MenuTabsConfigInputC = MenuTabsConfigC;
14
+ export function normalizeMenuTabsConfigInput(value) {
15
+ if (typeof value !== "object" || value === null) {
16
+ return {
17
+ menus: []
18
+ };
19
+ }
20
+ const menus = Reflect.get(value, "menus");
21
+ return {
22
+ menus: Array.isArray(menus) ? menus : []
23
+ };
24
+ }
@@ -0,0 +1,30 @@
1
+ import type { LocaleValue } from '../../../utils/coders.js';
2
+ import { type MenuTabsConfig } from '../menu-tabs/schema.js';
3
+ type __VLS_Props = {
4
+ config: MenuTabsConfig;
5
+ };
6
+ type __VLS_ModelProps = {
7
+ 'open'?: boolean;
8
+ };
9
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ "update:open": (value: boolean) => any;
12
+ confirm: (args_0: Readonly<{
13
+ menus: readonly Readonly<{
14
+ id: string;
15
+ title: LocaleValue;
16
+ to: string;
17
+ }>[];
18
+ }>) => any;
19
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
20
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
21
+ onConfirm?: ((args_0: Readonly<{
22
+ menus: readonly Readonly<{
23
+ id: string;
24
+ title: LocaleValue;
25
+ to: string;
26
+ }>[];
27
+ }>) => any) | undefined;
28
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
29
+ declare const _default: typeof __VLS_export;
30
+ export default _default;