@shwfed/config 2.12.5 → 2.12.7

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 (137) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +5 -0
  3. package/dist/runtime/components/config/blocks/2026-06-17/com.shwfed.block.tree.single/runtime.vue +54 -50
  4. package/dist/runtime/components/config/index.vue +23 -3
  5. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.text/config.d.vue.ts +22 -22
  6. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.text/config.vue.d.ts +22 -22
  7. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.combobox.single/config.d.vue.ts +16 -16
  8. package/dist/runtime/components/form/fields/2026-04-24/com.shwfed.form.field.combobox.single/config.vue.d.ts +16 -16
  9. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.date/config.d.vue.ts +16 -16
  10. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.date/config.vue.d.ts +16 -16
  11. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +24 -24
  12. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +24 -24
  13. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/config.d.vue.ts +16 -16
  14. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/config.vue.d.ts +16 -16
  15. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +26 -26
  16. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +26 -26
  17. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +2 -2
  18. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +2 -2
  19. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.d.vue.ts +30 -28
  20. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.vue +38 -0
  21. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/config.vue.d.ts +30 -28
  22. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/runtime.vue +11 -2
  23. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/schema.d.ts +3 -0
  24. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/schema.js +7 -0
  25. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.d.vue.ts +6 -6
  26. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue.d.ts +6 -6
  27. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.switch/config.d.vue.ts +22 -22
  28. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.switch/config.vue.d.ts +22 -22
  29. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.combobox.single.remote/config.d.vue.ts +16 -16
  30. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.combobox.single.remote/config.vue.d.ts +16 -16
  31. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.d.vue.ts +10 -10
  32. package/dist/runtime/components/form/fields/2026-05-23/com.shwfed.form.field.tree.multi/config.vue.d.ts +10 -10
  33. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/config.d.vue.ts +16 -16
  34. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/config.vue.d.ts +16 -16
  35. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.d.vue.ts +24 -24
  36. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.vue.d.ts +24 -24
  37. package/dist/runtime/components/form/fields/2026-05-25/com.shwfed.form.field.combobox.multi/config.d.vue.ts +16 -16
  38. package/dist/runtime/components/form/fields/2026-05-25/com.shwfed.form.field.combobox.multi/config.vue.d.ts +16 -16
  39. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +10 -10
  40. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +10 -10
  41. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +10 -10
  42. package/dist/runtime/components/form/fields/2026-05-26/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +10 -10
  43. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.d.vue.ts +10 -10
  44. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/config.vue.d.ts +10 -10
  45. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.d.vue.ts +10 -10
  46. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/config.vue.d.ts +10 -10
  47. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.d.vue.ts +10 -10
  48. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/config.vue.d.ts +10 -10
  49. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.d.vue.ts +10 -10
  50. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.single/config.vue.d.ts +10 -10
  51. package/dist/runtime/components/sidebar/index.d.vue.ts +22 -3
  52. package/dist/runtime/components/sidebar/index.vue +66 -5
  53. package/dist/runtime/components/sidebar/index.vue.d.ts +22 -3
  54. package/dist/runtime/components/sidebar/node.d.vue.ts +1 -1
  55. package/dist/runtime/components/sidebar/node.vue +33 -13
  56. package/dist/runtime/components/sidebar/node.vue.d.ts +1 -1
  57. package/dist/runtime/components/sidebar/tree.vue +113 -83
  58. package/dist/runtime/components/sidebar/types.d.ts +8 -0
  59. package/dist/runtime/components/sidebar/types.js +1 -0
  60. package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.number/runtime.vue +6 -73
  61. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  62. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  63. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/config.vue +44 -0
  64. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/runtime.vue +8 -2
  65. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/schema.d.ts +3 -0
  66. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/schema.js +7 -0
  67. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.d.vue.ts +6 -6
  68. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.vue.d.ts +6 -6
  69. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/config.d.vue.ts +2 -2
  70. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/config.vue.d.ts +2 -2
  71. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  72. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  73. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.d.vue.ts +6 -6
  74. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.vue.d.ts +6 -6
  75. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/config.d.vue.ts +2 -2
  76. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/config.vue.d.ts +2 -2
  77. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  78. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  79. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.d.vue.ts +2 -2
  80. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.vue.d.ts +2 -2
  81. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  82. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  83. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.d.vue.ts +2 -2
  84. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.vue.d.ts +2 -2
  85. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +10 -10
  86. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +10 -10
  87. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +10 -10
  88. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +10 -10
  89. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  90. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  91. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  92. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  93. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +10 -10
  94. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +10 -10
  95. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +10 -10
  96. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +10 -10
  97. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  98. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  99. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  100. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  101. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +10 -10
  102. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +10 -10
  103. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +10 -10
  104. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +10 -10
  105. package/dist/runtime/components/table/utils/row-reorder.d.ts +3 -2
  106. package/dist/runtime/components/table/utils/row-reorder.js +1 -12
  107. package/dist/runtime/components/tabs/index.d.vue.ts +9 -0
  108. package/dist/runtime/components/tabs/index.vue +92 -0
  109. package/dist/runtime/components/tabs/index.vue.d.ts +9 -0
  110. package/dist/runtime/components/tabs/tab.d.vue.ts +22 -0
  111. package/dist/runtime/components/tabs/tab.vue +85 -0
  112. package/dist/runtime/components/tabs/tab.vue.d.ts +22 -0
  113. package/dist/runtime/components/tabs/types.d.ts +19 -0
  114. package/dist/runtime/components/tabs/types.js +0 -0
  115. package/dist/runtime/components/tabs/use-reorder.d.ts +10 -0
  116. package/dist/runtime/components/tabs/use-reorder.js +90 -0
  117. package/dist/runtime/components/tabs/use-tabs.d.ts +46 -0
  118. package/dist/runtime/components/tabs/use-tabs.js +101 -0
  119. package/dist/runtime/components/ui/date-picker/DatePickerInput.d.vue.ts +1 -1
  120. package/dist/runtime/components/ui/date-picker/DatePickerInput.vue.d.ts +1 -1
  121. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.d.vue.ts +1 -1
  122. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.vue.d.ts +1 -1
  123. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.d.vue.ts +1 -1
  124. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.vue.d.ts +1 -1
  125. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +2 -2
  126. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +2 -2
  127. package/dist/runtime/components/ui/sidebar/SidebarMenuAction.vue +1 -1
  128. package/dist/runtime/components/ui/sidebar/index.js +2 -2
  129. package/dist/runtime/share/number-format.d.ts +7 -0
  130. package/dist/runtime/share/number-format.js +72 -0
  131. package/dist/runtime/share/reorder.d.ts +2 -0
  132. package/dist/runtime/share/reorder.js +12 -0
  133. package/dist/runtime/utils/nav-registry.d.ts +19 -0
  134. package/dist/runtime/utils/nav-registry.js +14 -0
  135. package/dist/runtime/utils/page-title-registry.d.ts +3 -0
  136. package/dist/runtime/utils/page-title-registry.js +11 -0
  137. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ <script setup>
2
+ import { onBeforeUnmount, onMounted } from "vue";
3
+ import { useI18n } from "vue-i18n";
4
+ import { navigateTo, useHead, useRoute, useRouter, useRuntimeConfig } from "#app";
5
+ import ShwfedTab from "./tab.vue";
6
+ import { useTabStore } from "./use-tabs";
7
+ import { useTabReorder } from "./use-reorder";
8
+ defineOptions({ name: "ShwfedTabs" });
9
+ const props = defineProps({
10
+ storageKey: { type: String, required: false },
11
+ home: { type: String, required: false, default: "/" }
12
+ });
13
+ const runtimeConfig = useRuntimeConfig();
14
+ const job = runtimeConfig.public.shwfed.ci.job;
15
+ const store = useTabStore(props.storageKey ?? `shwfed:tabs:${job ?? "default"}`);
16
+ const tabs = store.tabs;
17
+ const activePath = store.activePath;
18
+ const router = useRouter();
19
+ const route = useRoute();
20
+ const { locale } = useI18n({ useScope: "global", inheritLocale: true });
21
+ useHead(() => {
22
+ const tab = tabs.value.find((t) => t.path === activePath.value);
23
+ return tab ? { title: store.resolveTitle(tab, locale.value) } : {};
24
+ });
25
+ let stopAfterEach;
26
+ onMounted(() => {
27
+ store.openOrFocus(route.path, route.fullPath);
28
+ stopAfterEach = router.afterEach((to) => {
29
+ store.openOrFocus(to.path, to.fullPath);
30
+ });
31
+ });
32
+ onBeforeUnmount(() => stopAfterEach?.());
33
+ const reorder = useTabReorder({
34
+ onReorder: (sourcePath, targetPath, edge) => {
35
+ store.reorder(sourcePath, targetPath, edge === "left" ? "reorder-above" : "reorder-below");
36
+ }
37
+ });
38
+ function onActivate(tab) {
39
+ if (tab.path !== activePath.value) void navigateTo(tab.fullPath);
40
+ }
41
+ function onClose(path) {
42
+ const dest = store.close(path, props.home);
43
+ if (dest) void navigateTo(dest);
44
+ }
45
+ function onCloseOthers(path) {
46
+ const dest = store.closeOthers(path);
47
+ if (dest) void navigateTo(dest);
48
+ }
49
+ function onCloseRight(path) {
50
+ const dest = store.closeToRight(path);
51
+ if (dest) void navigateTo(dest);
52
+ }
53
+ function onWheel(event) {
54
+ const el = event.currentTarget;
55
+ if (!el) return;
56
+ if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return;
57
+ el.scrollLeft += event.deltaY;
58
+ event.preventDefault();
59
+ }
60
+ </script>
61
+
62
+ <template>
63
+ <ClientOnly>
64
+ <div
65
+ v-if="tabs.length"
66
+ class="w-full border-b border-zinc-200 bg-zinc-50"
67
+ >
68
+ <!-- Scroller: overflow-x with the native scrollbar hidden (Firefox +
69
+ WebKit). The vertical-wheel handler drives horizontal scroll, so no
70
+ scrollbar is shown at all. -->
71
+ <div
72
+ class="flex h-9 overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
73
+ @wheel="onWheel"
74
+ >
75
+ <ShwfedTab
76
+ v-for="tab in tabs"
77
+ :key="tab.path"
78
+ :title="store.resolveTitle(tab, locale)"
79
+ :active="tab.path === activePath"
80
+ :edge="reorder.edgeFor(tab.path)"
81
+ :dragging="reorder.isDragging(tab.path)"
82
+ :closable="tabs.length > 1"
83
+ :register="(el) => reorder.setTab(tab.path, el)"
84
+ @activate="onActivate(tab)"
85
+ @close="onClose(tab.path)"
86
+ @close-others="onCloseOthers(tab.path)"
87
+ @close-right="onCloseRight(tab.path)"
88
+ />
89
+ </div>
90
+ </div>
91
+ </ClientOnly>
92
+ </template>
@@ -0,0 +1,9 @@
1
+ type __VLS_Props = {
2
+ storageKey?: string;
3
+ home?: string;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
6
+ home: string;
7
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,22 @@
1
+ import type { TabEdge } from './use-reorder.js';
2
+ type __VLS_Props = {
3
+ title: string;
4
+ active: boolean;
5
+ edge: TabEdge | null;
6
+ dragging: boolean;
7
+ closable: boolean;
8
+ register: (el: HTMLElement | null) => void;
9
+ };
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ close: () => any;
12
+ activate: () => any;
13
+ closeOthers: () => any;
14
+ closeRight: () => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ onClose?: (() => any) | undefined;
17
+ onActivate?: (() => any) | undefined;
18
+ onCloseOthers?: (() => any) | undefined;
19
+ onCloseRight?: (() => any) | undefined;
20
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
@@ -0,0 +1,85 @@
1
+ <script setup>
2
+ import { onBeforeUnmount, onMounted, ref } from "vue";
3
+ import { Icon } from "@iconify/vue";
4
+ import { useI18n } from "vue-i18n";
5
+ import {
6
+ ContextMenu,
7
+ ContextMenuContent,
8
+ ContextMenuItem,
9
+ ContextMenuTrigger
10
+ } from "../ui/context-menu";
11
+ defineOptions({ name: "ShwfedTab" });
12
+ const props = defineProps({
13
+ title: { type: String, required: true },
14
+ active: { type: Boolean, required: true },
15
+ edge: { type: [String, null], required: true },
16
+ dragging: { type: Boolean, required: true },
17
+ closable: { type: Boolean, required: true },
18
+ register: { type: Function, required: true }
19
+ });
20
+ const emit = defineEmits(["activate", "close", "closeOthers", "closeRight"]);
21
+ const { t } = useI18n({
22
+ inheritLocale: true,
23
+ useScope: "global",
24
+ messages: {
25
+ zh: { close: "\u5173\u95ED", closeOthers: "\u5173\u95ED\u5176\u4ED6", closeRight: "\u5173\u95ED\u53F3\u4FA7" },
26
+ en: { close: "Close", closeOthers: "Close others", closeRight: "Close to the right" },
27
+ ja: { close: "\u9589\u3058\u308B", closeOthers: "\u4ED6\u3092\u9589\u3058\u308B", closeRight: "\u53F3\u5074\u3092\u9589\u3058\u308B" },
28
+ ko: { close: "\uB2EB\uAE30", closeOthers: "\uB2E4\uB978 \uD0ED \uB2EB\uAE30", closeRight: "\uC624\uB978\uCABD \uD0ED \uB2EB\uAE30" }
29
+ }
30
+ });
31
+ const root = ref(null);
32
+ onMounted(() => props.register(root.value));
33
+ onBeforeUnmount(() => props.register(null));
34
+ </script>
35
+
36
+ <template>
37
+ <ContextMenu>
38
+ <ContextMenuTrigger
39
+ as-child
40
+ :disabled="!closable"
41
+ >
42
+ <!-- Fixed width so consecutive closes keep every close button in the same
43
+ spot (Chrome-style). The close button stays in layout (`opacity-0`, not
44
+ hidden) and only reveals on hover / focus-within, so nothing reflows. -->
45
+ <div
46
+ ref="root"
47
+ class="group relative flex h-full w-40 shrink-0 cursor-default select-none items-center gap-1.5 border-r border-zinc-200 px-3 text-sm outline-none transition-colors"
48
+ :class="[
49
+ active ? 'bg-white text-zinc-900' : 'bg-zinc-50 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-600',
50
+ dragging ? 'opacity-50' : ''
51
+ ]"
52
+ @click="emit('activate')"
53
+ >
54
+ <span class="flex-1 truncate">{{ title }}</span>
55
+ <button
56
+ v-if="closable"
57
+ type="button"
58
+ class="flex size-4 shrink-0 items-center justify-center rounded-sm text-zinc-400 opacity-0 outline-none transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 hover:bg-zinc-200 hover:text-zinc-700"
59
+ @click.stop="emit('close')"
60
+ >
61
+ <Icon icon="fluent:dismiss-12-regular" />
62
+ </button>
63
+ <span
64
+ v-if="edge"
65
+ class="absolute inset-y-0 z-10 w-0.5 bg-(--primary)"
66
+ :class="edge === 'left' ? '-left-px' : '-right-px'"
67
+ />
68
+ </div>
69
+ </ContextMenuTrigger>
70
+ <ContextMenuContent class="min-w-44">
71
+ <ContextMenuItem @select="emit('close')">
72
+ <Icon icon="fluent:dismiss-20-regular" />
73
+ {{ t("close") }}
74
+ </ContextMenuItem>
75
+ <ContextMenuItem @select="emit('closeOthers')">
76
+ <Icon icon="fluent:dismiss-circle-20-regular" />
77
+ {{ t("closeOthers") }}
78
+ </ContextMenuItem>
79
+ <ContextMenuItem @select="emit('closeRight')">
80
+ <Icon icon="fluent:panel-right-20-regular" />
81
+ {{ t("closeRight") }}
82
+ </ContextMenuItem>
83
+ </ContextMenuContent>
84
+ </ContextMenu>
85
+ </template>
@@ -0,0 +1,22 @@
1
+ import type { TabEdge } from './use-reorder.js';
2
+ type __VLS_Props = {
3
+ title: string;
4
+ active: boolean;
5
+ edge: TabEdge | null;
6
+ dragging: boolean;
7
+ closable: boolean;
8
+ register: (el: HTMLElement | null) => void;
9
+ };
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ close: () => any;
12
+ activate: () => any;
13
+ closeOthers: () => any;
14
+ closeRight: () => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ onClose?: (() => any) | undefined;
17
+ onActivate?: (() => any) | undefined;
18
+ onCloseOthers?: (() => any) | undefined;
19
+ onCloseRight?: (() => any) | undefined;
20
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * One open tab in the app-level MDI strip.
3
+ *
4
+ * Identity is `path` alone — `/orders?status=open` and `/orders?status=closed`
5
+ * are the SAME `/orders` tab. Dedupe, ordering, persistence, and the context
6
+ * menu all key off `path`. The query is page state (read by the page via CEL
7
+ * `location`), never a tab-identity dimension.
8
+ *
9
+ * `fullPath` is the last full URL (path + query) the tab was navigated to, so
10
+ * returning to a tab restores its query view rather than dropping the query.
11
+ *
12
+ * `pinned` (no UI yet) marks a tab that bulk-close operations spare and that
13
+ * `close` refuses to remove — reserved for a future home / pinned-tab feature.
14
+ */
15
+ export interface Tab {
16
+ path: string;
17
+ fullPath: string;
18
+ pinned?: boolean;
19
+ }
File without changes
@@ -0,0 +1,10 @@
1
+ export type TabEdge = 'left' | 'right';
2
+ export interface UseTabReorderOptions {
3
+ onReorder: (sourcePath: string, targetPath: string, edge: TabEdge) => void;
4
+ }
5
+ export interface TabReorderApi {
6
+ setTab: (path: string, el: HTMLElement | null) => void;
7
+ edgeFor: (path: string) => TabEdge | null;
8
+ isDragging: (path: string) => boolean;
9
+ }
10
+ export declare function useTabReorder(options: UseTabReorderOptions): TabReorderApi;
@@ -0,0 +1,90 @@
1
+ import { onBeforeUnmount, ref } from "vue";
2
+ import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
3
+ import {
4
+ draggable,
5
+ dropTargetForElements
6
+ } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
7
+ import {
8
+ attachClosestEdge,
9
+ extractClosestEdge
10
+ } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
11
+ const SOURCE_KIND = "shwfed-tab";
12
+ export function useTabReorder(options) {
13
+ const indicator = ref(null);
14
+ const dragging = ref(null);
15
+ const regs = /* @__PURE__ */ new Map();
16
+ function rewire(path, reg) {
17
+ reg.cleanup?.();
18
+ if (!reg.el) {
19
+ reg.cleanup = null;
20
+ return;
21
+ }
22
+ const el = reg.el;
23
+ reg.cleanup = combine(
24
+ draggable({
25
+ element: el,
26
+ getInitialData: () => ({ kind: SOURCE_KIND, path }),
27
+ onDragStart: () => {
28
+ dragging.value = path;
29
+ },
30
+ onDrop: () => {
31
+ dragging.value = null;
32
+ if (indicator.value) indicator.value = null;
33
+ }
34
+ }),
35
+ dropTargetForElements({
36
+ element: el,
37
+ canDrop: ({ source }) => source.data.kind === SOURCE_KIND && source.data.path !== path,
38
+ getIsSticky: () => true,
39
+ getData: ({ input, element }) => attachClosestEdge(
40
+ { kind: `${SOURCE_KIND}-target`, path },
41
+ { input, element, allowedEdges: ["left", "right"] }
42
+ ),
43
+ onDrag: ({ self }) => {
44
+ const edge = extractClosestEdge(self.data);
45
+ if (edge === "left" || edge === "right") indicator.value = { path, edge };
46
+ else if (indicator.value?.path === path) indicator.value = null;
47
+ },
48
+ onDragLeave: () => {
49
+ if (indicator.value?.path === path) indicator.value = null;
50
+ },
51
+ onDrop: ({ source, self }) => {
52
+ indicator.value = null;
53
+ const edge = extractClosestEdge(self.data);
54
+ const sourcePath = typeof source.data.path === "string" ? source.data.path : null;
55
+ if ((edge === "left" || edge === "right") && sourcePath && sourcePath !== path) {
56
+ options.onReorder(sourcePath, path, edge);
57
+ }
58
+ }
59
+ })
60
+ );
61
+ }
62
+ function setTab(path, el) {
63
+ let reg = regs.get(path);
64
+ if (!reg) {
65
+ reg = { el: null, cleanup: null };
66
+ regs.set(path, reg);
67
+ }
68
+ if (reg.el === el) return;
69
+ reg.el = el;
70
+ rewire(path, reg);
71
+ if (!reg.el && !reg.cleanup) {
72
+ regs.delete(path);
73
+ if (indicator.value?.path === path) indicator.value = null;
74
+ if (dragging.value === path) dragging.value = null;
75
+ }
76
+ }
77
+ function edgeFor(path) {
78
+ return indicator.value?.path === path ? indicator.value.edge : null;
79
+ }
80
+ function isDragging(path) {
81
+ return dragging.value === path;
82
+ }
83
+ onBeforeUnmount(() => {
84
+ for (const reg of regs.values()) reg.cleanup?.();
85
+ regs.clear();
86
+ indicator.value = null;
87
+ dragging.value = null;
88
+ });
89
+ return { setTab, edgeFor, isDragging };
90
+ }
@@ -0,0 +1,46 @@
1
+ import { type Ref } from 'vue';
2
+ import { type ReorderInstruction } from '../../share/reorder.js';
3
+ import type { Tab } from './types.js';
4
+ /**
5
+ * The app-level tab store — a client singleton (per the module's "runtime
6
+ * singletons, not Nuxt injections" direction). Holds the open tabs (persisted to
7
+ * `sessionStorage` so they survive a refresh but die with the browser tab) and
8
+ * the resolved title logic.
9
+ *
10
+ * Deliberately framework-free: it never touches the router. Mutations that imply
11
+ * a navigation RETURN the `fullPath` the caller should navigate to (or `null`
12
+ * when no navigation is needed); the `ShwfedTabs` strip performs the actual
13
+ * `navigateTo`. This keeps the store trivially unit-testable and lets other
14
+ * call sites (the page `close` operation) reuse it without a router in scope.
15
+ */
16
+ export interface TabStore {
17
+ tabs: Ref<Tab[]>;
18
+ activePath: Ref<string | null>;
19
+ /** Ensure a tab for `path` exists and mark it active (route-change driven). */
20
+ openOrFocus: (path: string, fullPath: string) => void;
21
+ /** Remove a tab; returns the fullPath to navigate to when the active tab was closed. */
22
+ close: (path: string, home?: string) => string | null;
23
+ /** Keep only `path` (and pinned tabs); returns a nav target if the active tab was closed. */
24
+ closeOthers: (path: string) => string | null;
25
+ /** Close non-pinned tabs to the right of `path`; returns a nav target if active was closed. */
26
+ closeToRight: (path: string) => string | null;
27
+ reorder: (sourcePath: string, targetPath: string, instruction: ReorderInstruction) => void;
28
+ /**
29
+ * Title precedence: page title (by path) → nav label (query-specific by
30
+ * fullPath, else by path) → raw path. Takes the tab so the title can reflect
31
+ * the current query variant while identity stays path-only.
32
+ */
33
+ resolveTitle: (tab: {
34
+ path: string;
35
+ fullPath: string;
36
+ }, locale: string) => string;
37
+ hasTab: (path: string) => boolean;
38
+ }
39
+ export declare function createTabStore(storageKey: string): TabStore;
40
+ /**
41
+ * Access the shared tab store. Created once, in a detached effect scope so its
42
+ * `sessionStorage` reactivity is not disposed when the first component that
43
+ * touched it unmounts. `storageKey` only takes effect on first creation (there
44
+ * is one strip per app).
45
+ */
46
+ export declare function useTabStore(storageKey?: string): TabStore;
@@ -0,0 +1,101 @@
1
+ import { effectScope, ref } from "vue";
2
+ import { useSessionStorage } from "@vueuse/core";
3
+ import { getLocalizedText } from "../../share/locale.js";
4
+ import { readNavLabel } from "../../utils/nav-registry.js";
5
+ import { readPageTitle } from "../../utils/page-title-registry.js";
6
+ import { applyReorder } from "../../share/reorder.js";
7
+ export function createTabStore(storageKey) {
8
+ const tabs = useSessionStorage(storageKey, []);
9
+ const activePath = ref(null);
10
+ function indexOf(path) {
11
+ return tabs.value.findIndex((t) => t.path === path);
12
+ }
13
+ function openOrFocus(path, fullPath) {
14
+ const existing = tabs.value.find((t) => t.path === path);
15
+ if (existing) {
16
+ if (existing.fullPath !== fullPath) {
17
+ tabs.value = tabs.value.map((t) => t.path === path ? { ...t, fullPath } : t);
18
+ }
19
+ } else {
20
+ tabs.value = [...tabs.value, { path, fullPath }];
21
+ }
22
+ activePath.value = path;
23
+ }
24
+ function close(path, home) {
25
+ const idx = indexOf(path);
26
+ if (idx < 0) return null;
27
+ const target = tabs.value[idx];
28
+ if (!target || target.pinned) return null;
29
+ const wasActive = activePath.value === path;
30
+ const next = tabs.value.slice(0, idx).concat(tabs.value.slice(idx + 1));
31
+ tabs.value = next;
32
+ if (!wasActive) return null;
33
+ const neighbor = next[idx] ?? next[idx - 1] ?? null;
34
+ if (neighbor) {
35
+ activePath.value = neighbor.path;
36
+ return neighbor.fullPath;
37
+ }
38
+ activePath.value = null;
39
+ return home ?? null;
40
+ }
41
+ function closeOthers(path) {
42
+ tabs.value = tabs.value.filter((t) => t.pinned || t.path === path);
43
+ if (activePath.value === path) return null;
44
+ const target = tabs.value.find((t) => t.path === path);
45
+ if (target) {
46
+ activePath.value = path;
47
+ return target.fullPath;
48
+ }
49
+ return null;
50
+ }
51
+ function closeToRight(path) {
52
+ const idx = indexOf(path);
53
+ if (idx < 0) return null;
54
+ const removed = new Set(
55
+ tabs.value.slice(idx + 1).filter((t) => !t.pinned).map((t) => t.path)
56
+ );
57
+ if (removed.size === 0) return null;
58
+ tabs.value = tabs.value.filter((t) => !removed.has(t.path));
59
+ if (activePath.value && removed.has(activePath.value)) {
60
+ activePath.value = path;
61
+ return tabs.value.find((t) => t.path === path)?.fullPath ?? null;
62
+ }
63
+ return null;
64
+ }
65
+ function reorder(sourcePath, targetPath, instruction) {
66
+ const src = indexOf(sourcePath);
67
+ const tgt = indexOf(targetPath);
68
+ if (src < 0 || tgt < 0) return;
69
+ const next = applyReorder(tabs.value, src, tgt, instruction);
70
+ if (next !== tabs.value) tabs.value = next;
71
+ }
72
+ function resolveTitle(tab, locale) {
73
+ const fromPage = getLocalizedText(readPageTitle(tab.path), locale);
74
+ if (fromPage) return fromPage;
75
+ const fromNav = readNavLabel(tab.fullPath, tab.path)?.label;
76
+ if (fromNav) return fromNav;
77
+ return tab.path;
78
+ }
79
+ function hasTab(path) {
80
+ return indexOf(path) >= 0;
81
+ }
82
+ return {
83
+ tabs,
84
+ activePath,
85
+ openOrFocus,
86
+ close,
87
+ closeOthers,
88
+ closeToRight,
89
+ reorder,
90
+ resolveTitle,
91
+ hasTab
92
+ };
93
+ }
94
+ let singleton = null;
95
+ export function useTabStore(storageKey = "shwfed:tabs") {
96
+ if (!singleton) {
97
+ const scope = effectScope(true);
98
+ singleton = scope.run(() => createTabStore(storageKey));
99
+ }
100
+ return singleton;
101
+ }
@@ -28,8 +28,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
28
28
  "onUpdate:modelValue"?: ((args_0: Date | undefined) => any) | undefined;
29
29
  }>, {
30
30
  size: "sm" | "md" | "lg";
31
- clearable: boolean;
32
31
  clearIcon: string;
32
+ clearable: boolean;
33
33
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
34
34
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
35
35
  declare const _default: typeof __VLS_export;
@@ -28,8 +28,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
28
28
  "onUpdate:modelValue"?: ((args_0: Date | undefined) => any) | undefined;
29
29
  }>, {
30
30
  size: "sm" | "md" | "lg";
31
- clearable: boolean;
32
31
  clearIcon: string;
32
+ clearable: boolean;
33
33
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
34
34
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
35
35
  declare const _default: typeof __VLS_export;
@@ -22,8 +22,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
22
22
  size: "sm" | "md" | "lg";
23
23
  granularity: "hour" | "minute" | "second";
24
24
  hourCycle: 12 | 24;
25
- clearable: boolean;
26
25
  clearIcon: string;
26
+ clearable: boolean;
27
27
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
28
28
  declare const _default: typeof __VLS_export;
29
29
  export default _default;
@@ -22,8 +22,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
22
22
  size: "sm" | "md" | "lg";
23
23
  granularity: "hour" | "minute" | "second";
24
24
  hourCycle: 12 | 24;
25
- clearable: boolean;
26
25
  clearIcon: string;
26
+ clearable: boolean;
27
27
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
28
28
  declare const _default: typeof __VLS_export;
29
29
  export default _default;
@@ -29,8 +29,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
29
29
  "onUpdate:endValue"?: ((args_0: Date | undefined) => any) | undefined;
30
30
  }>, {
31
31
  size: "sm" | "md" | "lg";
32
- clearable: boolean;
33
32
  clearIcon: string;
33
+ clearable: boolean;
34
34
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
35
  declare const _default: typeof __VLS_export;
36
36
  export default _default;
@@ -29,8 +29,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
29
29
  "onUpdate:endValue"?: ((args_0: Date | undefined) => any) | undefined;
30
30
  }>, {
31
31
  size: "sm" | "md" | "lg";
32
- clearable: boolean;
33
32
  clearIcon: string;
33
+ clearable: boolean;
34
34
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
35
  declare const _default: typeof __VLS_export;
36
36
  export default _default;
@@ -26,9 +26,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
26
26
  size: "sm" | "md" | "lg";
27
27
  granularity: "hour" | "minute" | "second";
28
28
  hourCycle: 12 | 24;
29
- clearable: boolean;
30
- clearIcon: string;
31
29
  rangeSeparatorIcon: string;
30
+ clearIcon: string;
31
+ clearable: boolean;
32
32
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
33
  declare const _default: typeof __VLS_export;
34
34
  export default _default;
@@ -26,9 +26,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
26
26
  size: "sm" | "md" | "lg";
27
27
  granularity: "hour" | "minute" | "second";
28
28
  hourCycle: 12 | 24;
29
- clearable: boolean;
30
- clearIcon: string;
31
29
  rangeSeparatorIcon: string;
30
+ clearIcon: string;
31
+ clearable: boolean;
32
32
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
33
  declare const _default: typeof __VLS_export;
34
34
  export default _default;
@@ -14,7 +14,7 @@ const props = defineProps({
14
14
  data-slot="sidebar-menu-action"
15
15
  data-sidebar="menu-action"
16
16
  :class="cn(
17
- 'text-zinc-500 absolute top-1/2 right-0.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform [&>svg]:size-4 [&>svg]:shrink-0 cursor-pointer',
17
+ 'text-zinc-500 absolute top-1/2 right-0.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform [&>svg]:size-4 [&>svg]:shrink-0 cursor-default',
18
18
  'after:absolute after:-inset-2 md:after:hidden',
19
19
  'peer-data-[size=sm]/menu-button:top-1',
20
20
  'peer-data-[size=default]/menu-button:top-1.5',
@@ -24,11 +24,11 @@ export { default as SidebarSeparator } from "./SidebarSeparator.vue";
24
24
  export { default as SidebarTrigger } from "./SidebarTrigger.vue";
25
25
  export { useSidebar } from "./utils.js";
26
26
  export const sidebarMenuButtonVariants = cva(
27
- "peer/menu-button cursor-pointer flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden transition-[width,height,padding,color] duration-180 hover:bg-zinc-100 hover:text-zinc-900 active:bg-zinc-100 active:text-zinc-900 disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-[color-mix(in_srgb,var(--primary)_10%,white)] data-[active=true]:text-(--primary) data-[active=true]:font-mediumdata-[state=open]:hover:bg-zinc-100 data-[state=open]:hover:text-zinc-900 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
27
+ "peer/menu-button cursor-default flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden transition-[width,height,padding,color] duration-180 hover:bg-zinc-100 hover:text-zinc-900 active:bg-zinc-100 active:text-zinc-900 disabled:pointer-events-none disabled:opacity-50 group-has-[>[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-[color-mix(in_srgb,var(--primary)_10%,white)] data-[active=true]:text-(--primary) data-[active=true]:font-mediumdata-[state=open]:hover:bg-zinc-100 data-[state=open]:hover:text-zinc-900 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
28
28
  {
29
29
  variants: {
30
30
  variant: {
31
- default: "group-hover/menu-item:bg-zinc-100 group-hover/menu-item:text-zinc-900 group-focus-within/menu-item:bg-zinc-100 group-focus-within/menu-item:text-zinc-900 text-zinc-800",
31
+ default: "text-zinc-800",
32
32
  outline: "bg-white shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-zinc-100 hover:text-zinc-900 hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
33
33
  },
34
34
  size: {
@@ -0,0 +1,7 @@
1
+ export type NumberDisplayMode = 'plain' | 'uppercaseChinese' | 'monoThousand';
2
+ export declare function toChineseUppercase(n: number): string;
3
+ export declare function formatNumberDisplay(value: unknown, opts: {
4
+ displayMode?: NumberDisplayMode;
5
+ precision?: number;
6
+ roundingMode?: 'round' | 'floor' | 'ceil';
7
+ }): string;