vue-stack-tabs 1.0.1 → 1.3.0

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 (116) hide show
  1. package/README.en.md +526 -0
  2. package/README.md +337 -520
  3. package/README.ru.md +526 -0
  4. package/dist/iframe-bridge.d.ts +8 -0
  5. package/dist/iframe-bridge.html +77 -0
  6. package/dist/iframe-bridge.mjs +20 -0
  7. package/dist/iframe-child.html +88 -0
  8. package/dist/nuxt/favicon.svg +10 -0
  9. package/dist/nuxt/iframe-bridge.html +77 -0
  10. package/dist/nuxt/iframe-child.html +88 -0
  11. package/dist/nuxt/module.d.ts +6 -0
  12. package/dist/nuxt/module.mjs +28 -0
  13. package/dist/nuxt/plugin.mjs +9 -0
  14. package/dist/nuxt/runtime/plugin.d.ts +2 -0
  15. package/dist/nuxt/runtime/plugin.mjs +9 -0
  16. package/dist/src/lib/StackTabs.vue.d.ts +30 -16
  17. package/dist/src/lib/components/ContextMenu/ContextMenuItem.vue.d.ts +7 -1
  18. package/dist/src/lib/components/ContextMenu/index.vue.d.ts +5 -4
  19. package/dist/src/lib/components/PageLoading.vue.d.ts +2 -1
  20. package/dist/src/lib/components/StackKeepAlive/StackCacheRenderer.vue.d.ts +25 -0
  21. package/dist/src/lib/components/StackKeepAlive/StackKeepAlive.vue.d.ts +12 -0
  22. package/dist/src/lib/components/TabHeader/TabHeaderButton.vue.d.ts +11 -12
  23. package/dist/src/lib/components/TabHeader/TabHeaderItem.vue.d.ts +5 -4
  24. package/dist/src/lib/components/TabHeader/TabHeaderScroll.vue.d.ts +8 -22
  25. package/dist/src/lib/components/TabHeader/index.vue.d.ts +11 -164
  26. package/dist/src/lib/hooks/stackTabsContext.d.ts +49 -0
  27. package/dist/src/lib/hooks/tabPanel/evict.d.ts +21 -23
  28. package/dist/src/lib/hooks/tabPanel/navigationTransaction.d.ts +35 -0
  29. package/dist/src/lib/hooks/tabPanel/refresh.d.ts +17 -0
  30. package/dist/src/lib/hooks/tabPanel/scroll.d.ts +8 -8
  31. package/dist/src/lib/hooks/tabPanel/session.d.ts +9 -8
  32. package/dist/src/lib/hooks/tabPanel/state.d.ts +20 -73
  33. package/dist/src/lib/hooks/useIframeManager.d.ts +35 -0
  34. package/dist/src/lib/hooks/useTabActions.d.ts +5 -46
  35. package/dist/src/lib/hooks/useTabEventBus.d.ts +22 -9
  36. package/dist/src/lib/hooks/useTabPanel.d.ts +17 -74
  37. package/dist/src/lib/hooks/useTabRouter.d.ts +5 -5
  38. package/dist/src/lib/i18n/index.d.ts +6 -4
  39. package/dist/src/lib/i18n/lang/en.d.ts +5 -0
  40. package/dist/src/lib/i18n/lang/zh-CN.d.ts +5 -0
  41. package/dist/src/lib/i18n/stackTabsLocale.d.ts +2 -0
  42. package/dist/src/lib/iframe-bridge.d.ts +8 -0
  43. package/dist/src/lib/iframe.vue.d.ts +2 -1
  44. package/dist/src/lib/index.d.ts +4 -5
  45. package/dist/src/lib/model/TabModel.d.ts +14 -33
  46. package/dist/src/lib/nuxt/module.d.ts +6 -0
  47. package/dist/src/lib/nuxt/runtime/plugin.client.d.ts +2 -0
  48. package/dist/src/lib/nuxt/runtime/plugin.d.ts +2 -0
  49. package/dist/src/lib/utils/iframeBridge.d.ts +11 -3
  50. package/dist/src/lib/utils/stackTabsA11y.d.ts +8 -0
  51. package/dist/src/lib/utils/stackTabsMessage.d.ts +7 -0
  52. package/dist/src/lib/utils/tabInfoEncoder.d.ts +1 -16
  53. package/dist/src/lib/utils/typeGuards.d.ts +2 -0
  54. package/dist/src/lib/utils/urlParser.d.ts +24 -3
  55. package/dist/src/lib/versionLogger.d.ts +1 -0
  56. package/dist/style.css +2 -1
  57. package/dist/utils/iframeBridge.d.ts +39 -0
  58. package/dist/vue-stack-tabs.css +2 -1
  59. package/dist/vue-stack-tabs.es.js +1 -2411
  60. package/package.json +68 -47
  61. package/src/lib/StackTabs.vue +216 -192
  62. package/src/lib/assets/style/stackTab.scss +84 -12
  63. package/src/lib/components/ContextMenu/ContextMenuItem.vue +23 -11
  64. package/src/lib/components/ContextMenu/index.vue +94 -10
  65. package/src/lib/components/PageLoading.vue +9 -7
  66. package/src/lib/components/StackKeepAlive/StackCacheRenderer.vue +70 -0
  67. package/src/lib/components/StackKeepAlive/StackKeepAlive.vue +57 -0
  68. package/src/lib/components/TabHeader/TabHeaderButton.vue +19 -8
  69. package/src/lib/components/TabHeader/TabHeaderItem.vue +45 -16
  70. package/src/lib/components/TabHeader/TabHeaderScroll.vue +47 -25
  71. package/src/lib/components/TabHeader/index.vue +109 -12
  72. package/src/lib/hooks/stackTabsContext.ts +135 -0
  73. package/src/lib/hooks/tabPanel/evict.ts +123 -41
  74. package/src/lib/hooks/tabPanel/navigationTransaction.ts +61 -0
  75. package/src/lib/hooks/tabPanel/refresh.ts +109 -0
  76. package/src/lib/hooks/tabPanel/scroll.ts +57 -36
  77. package/src/lib/hooks/tabPanel/session.ts +84 -22
  78. package/src/lib/hooks/tabPanel/state.ts +41 -44
  79. package/src/lib/hooks/useIframeManager.ts +253 -0
  80. package/src/lib/hooks/useTabActions.ts +62 -30
  81. package/src/lib/hooks/useTabEventBus.ts +29 -19
  82. package/src/lib/hooks/useTabLoading.ts +3 -2
  83. package/src/lib/hooks/useTabPanel.tsx +415 -213
  84. package/src/lib/hooks/useTabRouter.ts +172 -43
  85. package/src/lib/i18n/index.ts +53 -45
  86. package/src/lib/i18n/lang/en.ts +6 -1
  87. package/src/lib/i18n/lang/zh-CN.ts +6 -1
  88. package/src/lib/i18n/stackTabsLocale.ts +8 -0
  89. package/src/lib/iframe-bridge.ts +12 -0
  90. package/src/lib/index.ts +14 -19
  91. package/src/lib/model/TabModel.ts +34 -86
  92. package/src/lib/nuxt/module.d.ts +4 -0
  93. package/src/lib/nuxt/module.ts +8 -3
  94. package/src/lib/nuxt/runtime/plugin.client.ts +2 -3
  95. package/src/lib/nuxt/runtime/plugin.ts +11 -0
  96. package/src/lib/utils/iframeBridge.ts +29 -5
  97. package/src/lib/utils/scrollUtils.ts +9 -4
  98. package/src/lib/utils/stackTabsA11y.ts +12 -0
  99. package/src/lib/utils/stackTabsMessage.ts +99 -0
  100. package/src/lib/utils/tabInfoEncoder.ts +69 -42
  101. package/src/lib/utils/typeGuards.ts +3 -0
  102. package/src/lib/utils/urlParser.ts +144 -14
  103. package/src/lib/versionLogger.ts +7 -19
  104. package/dist/src/lib/hooks/tabPanel/evict.spec.d.ts +0 -1
  105. package/dist/src/lib/hooks/useTabPanel.spec.d.ts +0 -1
  106. package/dist/src/lib/model/TabModel.spec.d.ts +0 -1
  107. package/dist/src/lib/utils/iframeBridge.spec.d.ts +0 -1
  108. package/dist/src/lib/utils/tabInfoEncoder.spec.d.ts +0 -1
  109. package/dist/src/lib/utils/urlParser.spec.d.ts +0 -1
  110. package/dist/vue-stack-tabs.umd.js +0 -1
  111. package/src/lib/hooks/tabPanel/evict.spec.ts +0 -85
  112. package/src/lib/hooks/useTabPanel.spec.ts +0 -34
  113. package/src/lib/model/TabModel.spec.ts +0 -40
  114. package/src/lib/utils/iframeBridge.spec.ts +0 -37
  115. package/src/lib/utils/tabInfoEncoder.spec.ts +0 -42
  116. package/src/lib/utils/urlParser.spec.ts +0 -45
@@ -1,4 +1,4 @@
1
- import { nextTick, TransitionProps } from 'vue';
1
+ import { TransitionProps } from 'vue';
2
2
  import { TabScrollMode } from '../../model/TabModel';
3
3
  type __VLS_Props = {
4
4
  space: number;
@@ -7,92 +7,13 @@ type __VLS_Props = {
7
7
  contextmenu?: boolean | object;
8
8
  max?: number;
9
9
  };
10
- declare function __VLS_template(): {
11
- attrs: Partial<{}>;
12
- slots: {
13
- leftButton?(_: {}): any;
14
- rightButton?(_: {}): any;
15
- };
16
- refs: {
17
- scrollContainerRef: ({
18
- $: import('vue').ComponentInternalInstance;
19
- $data: {};
20
- $props: {
21
- readonly space?: number | undefined;
22
- readonly isScrollWheel?: boolean | undefined;
23
- readonly isScrollButton?: boolean | undefined;
24
- } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps;
25
- $attrs: {
26
- [x: string]: unknown;
27
- };
28
- $refs: {
29
- [x: string]: unknown;
30
- } & {
31
- headerScroll: HTMLDivElement;
32
- container: HTMLDivElement;
33
- bar: HTMLDivElement;
34
- thumb: HTMLDivElement;
35
- };
36
- $slots: Readonly<{
37
- [name: string]: import('vue').Slot<any> | undefined;
38
- }>;
39
- $root: import('vue').ComponentPublicInstance | null;
40
- $parent: import('vue').ComponentPublicInstance | null;
41
- $host: Element | null;
42
- $emit: (event: string, ...args: any[]) => void;
43
- $el: any;
44
- $options: import('vue').ComponentOptionsBase<Readonly<{
45
- space?: number;
46
- isScrollWheel?: boolean;
47
- isScrollButton?: boolean;
48
- }> & Readonly<{}>, {
49
- scrollIntoView: (el: HTMLElement) => void;
50
- isInView: (el: HTMLElement) => boolean;
51
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, {
52
- space: number;
53
- isScrollWheel: boolean;
54
- isScrollButton: boolean;
55
- }, {}, string, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, import('vue').ComponentProvideOptions> & {
56
- beforeCreate?: (() => void) | (() => void)[];
57
- created?: (() => void) | (() => void)[];
58
- beforeMount?: (() => void) | (() => void)[];
59
- mounted?: (() => void) | (() => void)[];
60
- beforeUpdate?: (() => void) | (() => void)[];
61
- updated?: (() => void) | (() => void)[];
62
- activated?: (() => void) | (() => void)[];
63
- deactivated?: (() => void) | (() => void)[];
64
- beforeDestroy?: (() => void) | (() => void)[];
65
- beforeUnmount?: (() => void) | (() => void)[];
66
- destroyed?: (() => void) | (() => void)[];
67
- unmounted?: (() => void) | (() => void)[];
68
- renderTracked?: ((e: import('vue').DebuggerEvent) => void) | ((e: import('vue').DebuggerEvent) => void)[];
69
- renderTriggered?: ((e: import('vue').DebuggerEvent) => void) | ((e: import('vue').DebuggerEvent) => void)[];
70
- errorCaptured?: ((err: unknown, instance: import('vue').ComponentPublicInstance | null, info: string) => boolean | void) | ((err: unknown, instance: import('vue').ComponentPublicInstance | null, info: string) => boolean | void)[];
71
- };
72
- $forceUpdate: () => void;
73
- $nextTick: typeof nextTick;
74
- $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infer R ? (...args: [R, R, import('@vue/reactivity').OnCleanup]) => any : (...args: [any, any, import('@vue/reactivity').OnCleanup]) => any, options?: import('vue').WatchOptions): import('vue').WatchStopHandle;
75
- } & Readonly<{
76
- space: number;
77
- isScrollWheel: boolean;
78
- isScrollButton: boolean;
79
- }> & Omit<Readonly<{
80
- space?: number;
81
- isScrollWheel?: boolean;
82
- isScrollButton?: boolean;
83
- }> & Readonly<{}>, "scrollIntoView" | "isInView" | ("space" | "isScrollWheel" | "isScrollButton")> & import('vue').ShallowUnwrapRef<{
84
- scrollIntoView: (el: HTMLElement) => void;
85
- isInView: (el: HTMLElement) => boolean;
86
- }> & {} & import('vue').ComponentCustomProperties & {} & {
87
- $slots: {
88
- default?(_: {}): any;
89
- };
90
- }) | null;
91
- };
92
- rootEl: HTMLDivElement;
10
+ declare var __VLS_1: {}, __VLS_30: {};
11
+ type __VLS_Slots = {} & {
12
+ leftButton?: (props: typeof __VLS_1) => any;
13
+ } & {
14
+ rightButton?: (props: typeof __VLS_30) => any;
93
15
  };
94
- type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
95
- declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
16
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
96
17
  active: (...args: any[]) => void;
97
18
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
98
19
  onActive?: ((...args: any[]) => any) | undefined;
@@ -101,85 +22,11 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}
101
22
  contextmenu: boolean | object;
102
23
  tabScrollMode: TabScrollMode;
103
24
  tabTransition: string | TransitionProps;
104
- }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
105
- scrollContainerRef: ({
106
- $: import('vue').ComponentInternalInstance;
107
- $data: {};
108
- $props: {
109
- readonly space?: number | undefined;
110
- readonly isScrollWheel?: boolean | undefined;
111
- readonly isScrollButton?: boolean | undefined;
112
- } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps;
113
- $attrs: {
114
- [x: string]: unknown;
115
- };
116
- $refs: {
117
- [x: string]: unknown;
118
- } & {
119
- headerScroll: HTMLDivElement;
120
- container: HTMLDivElement;
121
- bar: HTMLDivElement;
122
- thumb: HTMLDivElement;
123
- };
124
- $slots: Readonly<{
125
- [name: string]: import('vue').Slot<any> | undefined;
126
- }>;
127
- $root: import('vue').ComponentPublicInstance | null;
128
- $parent: import('vue').ComponentPublicInstance | null;
129
- $host: Element | null;
130
- $emit: (event: string, ...args: any[]) => void;
131
- $el: any;
132
- $options: import('vue').ComponentOptionsBase<Readonly<{
133
- space?: number;
134
- isScrollWheel?: boolean;
135
- isScrollButton?: boolean;
136
- }> & Readonly<{}>, {
137
- scrollIntoView: (el: HTMLElement) => void;
138
- isInView: (el: HTMLElement) => boolean;
139
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, {
140
- space: number;
141
- isScrollWheel: boolean;
142
- isScrollButton: boolean;
143
- }, {}, string, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, import('vue').ComponentProvideOptions> & {
144
- beforeCreate?: (() => void) | (() => void)[];
145
- created?: (() => void) | (() => void)[];
146
- beforeMount?: (() => void) | (() => void)[];
147
- mounted?: (() => void) | (() => void)[];
148
- beforeUpdate?: (() => void) | (() => void)[];
149
- updated?: (() => void) | (() => void)[];
150
- activated?: (() => void) | (() => void)[];
151
- deactivated?: (() => void) | (() => void)[];
152
- beforeDestroy?: (() => void) | (() => void)[];
153
- beforeUnmount?: (() => void) | (() => void)[];
154
- destroyed?: (() => void) | (() => void)[];
155
- unmounted?: (() => void) | (() => void)[];
156
- renderTracked?: ((e: import('vue').DebuggerEvent) => void) | ((e: import('vue').DebuggerEvent) => void)[];
157
- renderTriggered?: ((e: import('vue').DebuggerEvent) => void) | ((e: import('vue').DebuggerEvent) => void)[];
158
- errorCaptured?: ((err: unknown, instance: import('vue').ComponentPublicInstance | null, info: string) => boolean | void) | ((err: unknown, instance: import('vue').ComponentPublicInstance | null, info: string) => boolean | void)[];
159
- };
160
- $forceUpdate: () => void;
161
- $nextTick: typeof nextTick;
162
- $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infer R ? (...args: [R, R, import('@vue/reactivity').OnCleanup]) => any : (...args: [any, any, import('@vue/reactivity').OnCleanup]) => any, options?: import('vue').WatchOptions): import('vue').WatchStopHandle;
163
- } & Readonly<{
164
- space: number;
165
- isScrollWheel: boolean;
166
- isScrollButton: boolean;
167
- }> & Omit<Readonly<{
168
- space?: number;
169
- isScrollWheel?: boolean;
170
- isScrollButton?: boolean;
171
- }> & Readonly<{}>, "scrollIntoView" | "isInView" | ("space" | "isScrollWheel" | "isScrollButton")> & import('vue').ShallowUnwrapRef<{
172
- scrollIntoView: (el: HTMLElement) => void;
173
- isInView: (el: HTMLElement) => boolean;
174
- }> & {} & import('vue').ComponentCustomProperties & {} & {
175
- $slots: {
176
- default?(_: {}): any;
177
- };
178
- }) | null;
179
- }, HTMLDivElement>;
180
- declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
25
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
26
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
27
+ declare const _default: typeof __VLS_export;
181
28
  export default _default;
182
- type __VLS_WithTemplateSlots<T, S> = T & {
29
+ type __VLS_WithSlots<T, S> = T & {
183
30
  new (): {
184
31
  $slots: S;
185
32
  };
@@ -0,0 +1,49 @@
1
+ import { App, DefineComponent, InjectionKey, Ref, ShallowRef } from 'vue';
2
+ import { Emitter } from 'mitt';
3
+ import { ITabItem } from '../model/TabModel';
4
+ import { TabEventPayloadMap } from './useTabEventBus';
5
+ import { ScrollPosition } from './tabPanel/state';
6
+ export declare const STACK_TABS_CONTEXT_UNAVAILABLE_MESSAGE = "VueStackTabs runtime context is not available. Render <VueStackTabs> before using vue-stack-tabs composables.";
7
+ export declare const STACK_TABS_DUPLICATE_INSTANCE_MESSAGE = "VueStackTabs only supports one <VueStackTabs> instance per Vue app.";
8
+ export interface StackTabsRuntimeContext {
9
+ tabs: Ref<ITabItem[]>;
10
+ defaultTabs: Ref<ITabItem[]>;
11
+ caches: ShallowRef<string[]>;
12
+ components: Map<string, DefineComponent>;
13
+ cacheIdsToEvict: Set<string>;
14
+ tabIdsToEvict: Set<string>;
15
+ refreshKey: Ref<number>;
16
+ scrollPositionsByPageId: Map<string, Map<string, ScrollPosition>>;
17
+ isInitialized: Ref<boolean>;
18
+ iframeRefreshKeys: Ref<Record<string, number>>;
19
+ maxTabCount: Ref<number>;
20
+ useGlobalScroll: Ref<boolean>;
21
+ sessionPrefix: Ref<string>;
22
+ iframePath: Ref<string>;
23
+ eventBus: Emitter<TabEventPayloadMap>;
24
+ }
25
+ export interface CreateStackTabsRuntimeContextOptions {
26
+ iframePath?: string;
27
+ maxTabCount?: number;
28
+ useGlobalScroll?: boolean;
29
+ sessionPrefix?: string;
30
+ }
31
+ export interface RegisterStackTabsRuntimeContextOptions {
32
+ app?: App;
33
+ isProduction?: boolean;
34
+ }
35
+ export interface UnregisterStackTabsRuntimeContextOptions {
36
+ app?: App;
37
+ }
38
+ export declare const stackTabsContextKey: InjectionKey<StackTabsRuntimeContext>;
39
+ export declare const maximumKey: InjectionKey<Ref<boolean>>;
40
+ /** 创建一个新的运行时上下文实例 */
41
+ export declare const createStackTabsRuntimeContext: (options?: CreateStackTabsRuntimeContextOptions) => StackTabsRuntimeContext;
42
+ /** 获取当前活跃的运行时上下文(模块级单例) */
43
+ export declare const getActiveStackTabsRuntimeContext: () => StackTabsRuntimeContext | null;
44
+ /** 注册运行时上下文,重复注册会在开发环境抛出错误 */
45
+ export declare const registerStackTabsRuntimeContext: (context: StackTabsRuntimeContext, options?: RegisterStackTabsRuntimeContextOptions) => boolean;
46
+ /** 注销运行时上下文,组件卸载时调用 */
47
+ export declare const unregisterStackTabsRuntimeContext: (context: StackTabsRuntimeContext, options?: UnregisterStackTabsRuntimeContextOptions) => void;
48
+ /** 解析当前可用的运行时上下文,优先使用 inject,回退到模块单例 */
49
+ export declare const resolveStackTabsRuntimeContext: () => StackTabsRuntimeContext;
@@ -1,24 +1,22 @@
1
1
  import { ITabPage } from '../../model/TabModel';
2
- /** 标记缓存为待驱逐 */
3
- export declare const markCacheForEviction: (cacheName: string) => void;
4
- /** 标记标签下所有页面为可驱逐,并标记该标签(关闭场景) */
5
- export declare const markTabPagesForEviction: (tab: {
6
- id: string;
7
- pages: {
8
- list(): ITabPage[];
9
- };
10
- }) => void;
11
- /** 仅标记标签下所有页面为可驱逐(续签场景) */
12
- export declare const markTabPagesForEvictionOnly: (tab: {
13
- pages: {
14
- list(): ITabPage[];
15
- };
16
- }) => void;
17
- /** 将缓存 id 加入 caches */
18
- export declare const addCache: (cacheName: string) => void;
19
- /** 从 caches 中移除指定 id */
20
- export declare const removeCache: (cacheName: string) => void;
21
- /** 立即驱逐单个页面缓存 */
22
- export declare const evictPageCache: (cacheName: string) => void;
23
- /** 执行所有待驱逐缓存的驱逐 */
24
- export declare const evictMarkedCaches: () => void;
2
+ import { StackTabsRuntimeContext } from '../stackTabsContext';
3
+ export interface TabPanelEvictionApi {
4
+ markCacheForEviction: (cacheName: string) => void;
5
+ markTabPagesForEviction: (tab: {
6
+ id: string;
7
+ pages: {
8
+ list(): ITabPage[];
9
+ };
10
+ }) => void;
11
+ markTabPagesForEvictionOnly: (tab: {
12
+ pages: {
13
+ list(): ITabPage[];
14
+ };
15
+ }) => void;
16
+ addCache: (cacheName: string) => void;
17
+ removeCache: (cacheName: string) => void;
18
+ evictPageCache: (cacheName: string) => void;
19
+ replacePageCaches: (cacheNamesToEvict: Iterable<string>, cacheNamesToAdd: Iterable<string>) => void;
20
+ evictMarkedCaches: () => void;
21
+ }
22
+ export declare const createTabPanelEviction: (context: StackTabsRuntimeContext) => TabPanelEvictionApi;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 导航事务管理器。
3
+ *
4
+ * 将「乐观 UI 更新 → 路由导航 → 确认/回滚」包装为原子事务:
5
+ * 1. apply() — 先对 UI 状态做乐观修改,返回快照
6
+ * 2. navigate() — 执行 router.push 等异步导航
7
+ * 3. 成功 → commit();失败/异常 → rollback(snapshot) 恢复快照
8
+ *
9
+ * 当连续快速操作导致事务过期时(isCurrent 返回 false),
10
+ * 不回滚而是调用 cleanupStale() 清理残留状态,避免覆盖新事务的结果。
11
+ */
12
+ interface NavigationTransactionOptions<TSnapshot, TResult> {
13
+ /** 乐观应用 UI 变更,返回可用于 rollback 的快照 */
14
+ apply: () => TSnapshot;
15
+ /** 执行异步路由导航 */
16
+ navigate: () => Promise<TResult>;
17
+ /** 导航失败时回滚到快照状态 */
18
+ rollback: (snapshot: TSnapshot) => void;
19
+ /** 导航成功后的确认回调(如驱逐旧缓存) */
20
+ commit?: () => void;
21
+ /** 判断导航结果是否为失败(如 vue-router 返回的 NavigationFailure) */
22
+ isFailureResult?: (result: TResult) => boolean;
23
+ /** 若为失败结果,是否将其作为异常抛出 */
24
+ rejectFailureResult?: boolean;
25
+ /** 检查当前事务是否仍然有效(防止过期事务覆盖新状态) */
26
+ isCurrent?: () => boolean;
27
+ /** 事务已过期时的清理回调(替代 rollback) */
28
+ cleanupStale?: () => void;
29
+ }
30
+ /**
31
+ * 执行一次导航事务:apply → navigate → commit/rollback。
32
+ * 保证乐观更新在导航失败时能安全回滚。
33
+ */
34
+ export declare const runNavigationTransaction: <TSnapshot, TResult>(options: NavigationTransactionOptions<TSnapshot, TResult>) => Promise<TResult>;
35
+ export {};
@@ -0,0 +1,17 @@
1
+ import { Ref } from 'vue';
2
+ import { ITabItem } from '../../model/TabModel';
3
+ export interface TabPanelRefreshApi {
4
+ refreshTab: (id: string) => void;
5
+ refreshAllTabs: () => void;
6
+ }
7
+ export interface CreateTabPanelRefreshOptions {
8
+ tabs: Ref<ITabItem[]>;
9
+ iframeRefreshKeys: Ref<Record<string, number>>;
10
+ getTab: (id: string) => ITabItem | null;
11
+ createPageId: () => string;
12
+ addCache: (cacheName: string) => void;
13
+ evictPageCache: (cacheName: string) => void;
14
+ replacePageCaches: (cacheNamesToEvict: Iterable<string>, cacheNamesToAdd: Iterable<string>) => void;
15
+ refreshIframeByPostMessage: (tabId: string) => void;
16
+ }
17
+ export declare const createTabPanelRefresh: (options: CreateTabPanelRefreshOptions) => TabPanelRefreshApi;
@@ -1,8 +1,8 @@
1
- /** 恢复指定页面的滚动位置 */
2
- export declare const restoreScroller: (pageCacheId: string) => void;
3
- /** 保存指定页面的滚动位置 */
4
- export declare const saveScroller: (pageCacheId: string) => void;
5
- /** 移除指定页面的滚动记录 */
6
- export declare const removeScroller: (pageCacheId: string) => void;
7
- /** 为页面注册需要记录滚动位置的选择器 */
8
- export declare const addPageScroller: (pageCacheId: string, ...selectorIds: string[]) => void;
1
+ import { StackTabsRuntimeContext } from '../stackTabsContext';
2
+ export interface TabPanelScrollApi {
3
+ restoreScroller: (pageCacheId: string) => void;
4
+ saveScroller: (pageCacheId: string) => void;
5
+ removeScroller: (pageCacheId: string) => void;
6
+ addPageScroller: (pageCacheId: string, ...selectorIds: string[]) => void;
7
+ }
8
+ export declare const createTabPanelScroll: (context: StackTabsRuntimeContext) => TabPanelScrollApi;
@@ -1,9 +1,10 @@
1
1
  import { ITabItem } from '../../model/TabModel';
2
- /** 获取 sessionStorage 的 key */
3
- export declare const getSessionKey: () => string;
4
- /** 将当前激活的标签保存到 sessionStorage */
5
- export declare const saveActiveTabToSession: (tab: ITabItem) => void;
6
- /** 清除 sessionStorage 中的激活标签 */
7
- export declare const clearSession: () => void;
8
- /** 从 sessionStorage 解析恢复的标签(含 Stack 反序列化) */
9
- export declare const restoreTabFromSession: (storedJson: string | null) => ITabItem | null;
2
+ import { StackTabsRuntimeContext } from '../stackTabsContext';
3
+ export interface TabPanelSessionApi {
4
+ getSessionKey: () => string;
5
+ saveActiveTabToSession: (tab: ITabItem) => void;
6
+ clearSession: () => void;
7
+ restoreActiveTabSession: (storedJson: string | null) => void;
8
+ restoreTabFromSession: (storedJson: string | null) => ITabItem | null;
9
+ }
10
+ export declare const createTabPanelSession: (context: StackTabsRuntimeContext) => TabPanelSessionApi;
@@ -1,77 +1,24 @@
1
+ import { DefineComponent, Ref, ShallowRef } from 'vue';
1
2
  import { ITabItem } from '../../model/TabModel';
2
- /** 当前打开的标签列表(含每个标签的页面栈) */
3
- export declare const tabs: import('vue').Ref<{
4
- id: string;
5
- closable: boolean;
6
- refreshable: boolean;
7
- iframe: boolean;
8
- iframeRefreshMode?: import('../..').IframeRefreshMode | undefined;
9
- url?: string | undefined;
10
- active: boolean;
11
- pages: {
12
- push: (element: import('../..').ITabPage) => void;
13
- pop: () => import('../..').ITabPage | undefined;
14
- peek: () => import('../..').ITabPage | undefined;
15
- isEmpty: () => boolean;
16
- size: () => number;
17
- clear: () => void;
18
- list: () => import('../..').ITabPage[];
19
- toString: () => string;
20
- toJSON: () => import('../..').ITabPage[];
21
- };
22
- title: string;
23
- }[], ITabItem[] | {
24
- id: string;
25
- closable: boolean;
26
- refreshable: boolean;
27
- iframe: boolean;
28
- iframeRefreshMode?: import('../..').IframeRefreshMode | undefined;
29
- url?: string | undefined;
30
- active: boolean;
31
- pages: {
32
- push: (element: import('../..').ITabPage) => void;
33
- pop: () => import('../..').ITabPage | undefined;
34
- peek: () => import('../..').ITabPage | undefined;
35
- isEmpty: () => boolean;
36
- size: () => number;
37
- clear: () => void;
38
- list: () => import('../..').ITabPage[];
39
- toString: () => string;
40
- toJSON: () => import('../..').ITabPage[];
41
- };
42
- title: string;
43
- }[]>;
44
- /** 初始化时传入的默认标签(用于对比 session 恢复) */
45
- export declare const defaultTabs: ITabItem[];
46
- /** keep-alive include 数组,与 Vue 的 keep-alive :include 绑定 */
47
- export declare const caches: import('vue').Ref<string[], string[]>;
48
- /** 页面缓存 ID -> 包装后的 DefineComponent */
49
- export declare const components: Map<string, import('vue').DefineComponent<{}, {}, {}, import('vue').ComputedOptions, import('vue').MethodOptions, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{}>>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>>;
50
- /** 待驱逐的页面缓存 ID 集合 */
51
- export declare const cacheIdsToEvict: Set<string>;
52
- /** 待驱逐的标签 ID 集合 */
53
- export declare const tabIdsToEvict: Set<string>;
54
- /** 刷新计数器,变更时触发 component :key 变化 */
55
- export declare const refreshKey: import('vue').Ref<number, number>;
56
- /** 刷新时临时加入 keep-alive exclude 的缓存 ID */
57
- export declare const excludedCacheIdsForRefresh: import('vue').Ref<string[], string[]>;
58
- /** pageCacheId -> Map<selector, { top, left }> 滚动位置存储 */
59
- export declare const scrollPositionsByPageId: Map<string, Map<string, {
3
+ export interface ScrollPosition {
60
4
  top: number;
61
5
  left: number;
62
- }>>;
63
- /** 是否已完成初始化 */
64
- export declare const isInitialized: import('vue').Ref<boolean, boolean>;
65
- /** sessionStorage 中存储当前激活标签的 key 前缀 */
6
+ }
7
+ export interface TabPanelRuntimeState {
8
+ tabs: Ref<ITabItem[]>;
9
+ defaultTabs: Ref<ITabItem[]>;
10
+ caches: ShallowRef<string[]>;
11
+ components: Map<string, DefineComponent>;
12
+ cacheIdsToEvict: Set<string>;
13
+ tabIdsToEvict: Set<string>;
14
+ refreshKey: Ref<number>;
15
+ scrollPositionsByPageId: Map<string, Map<string, ScrollPosition>>;
16
+ isInitialized: Ref<boolean>;
17
+ iframeRefreshKeys: Ref<Record<string, number>>;
18
+ maxTabCount: Ref<number>;
19
+ useGlobalScroll: Ref<boolean>;
20
+ sessionPrefix: Ref<string>;
21
+ }
22
+ /** sessionStorage 中存储当前激活标签的 key 后缀 */
66
23
  export declare const SESSION_TAB_NAME = "stacktab-active-tab";
67
- /** iframe 标签 tabId -> 刷新序号,变更时触发 iframe 重载 */
68
- export declare const iframeRefreshKeys: import('vue').Ref<Record<string, number>, Record<string, number>>;
69
- /** 最大标签数量,0 表示不限制 */
70
- export declare let maxTabCount: number;
71
- /** 是否使用页面级滚动 */
72
- export declare let useGlobalScroll: boolean;
73
- /** sessionStorage key 前缀 */
74
- export declare let sessionPrefix: string;
75
- export declare const setMaxTabCount: (n: number) => void;
76
- export declare const setUseGlobalScroll: (v: boolean) => void;
77
- export declare const setSessionPrefix: (p: string) => void;
24
+ export declare const createTabPanelRuntimeState: () => TabPanelRuntimeState;
@@ -0,0 +1,35 @@
1
+ import { Ref } from 'vue';
2
+ import { ITabData, ITabItem } from '../model/TabModel';
3
+ type IframeLoadStatus = 'idle' | 'loading' | 'loaded' | 'timeout';
4
+ interface IframeLoadState {
5
+ status: IframeLoadStatus;
6
+ message?: string;
7
+ }
8
+ export interface UseIframeManagerOptions {
9
+ tabs: Ref<ITabItem[]>;
10
+ iframeRefreshKeys: Ref<Record<string, number>>;
11
+ iframeLoadTimeout: number;
12
+ t: (key: string) => string;
13
+ }
14
+ export declare function useIframeManager(options: UseIframeManagerOptions): {
15
+ iframeTabs: import('vue').ComputedRef<ITabItem[]>;
16
+ activeNonIframeTab: import('vue').ComputedRef<ITabItem | undefined>;
17
+ hasActiveNonIframeTab: import('vue').ComputedRef<boolean>;
18
+ iframeSrcMap: import('vue').ComputedRef<Record<string, string>>;
19
+ getIframeRefreshKey: (frameId: string) => number;
20
+ getIframeKey: (frame: {
21
+ id: string;
22
+ }) => string;
23
+ getIframeLoadState: (frameId: string) => IframeLoadState;
24
+ shouldShowIframeLoading: (frameId: string) => boolean;
25
+ shouldShowIframeError: (frameId: string) => boolean;
26
+ retryIframe: (frameId: string) => void;
27
+ setIframeRef: (id: string, el: HTMLIFrameElement | null) => void;
28
+ handleIframeLoad: (frame: {
29
+ id: string;
30
+ url?: string;
31
+ }, event: Event) => void;
32
+ handleRefreshIframePostMessage: (tabId: string) => void;
33
+ createMessageHandler: (allowedOrigins: Ref<string[]>, openTab: (tab: ITabData) => Promise<string | undefined>) => (ev: MessageEvent) => void;
34
+ };
35
+ export {};
@@ -1,5 +1,4 @@
1
- import { throttle } from 'throttle-debounce';
2
- import { ITabData, IframeRefreshMode } from '../model/TabModel';
1
+ import { ITabData } from '../model/TabModel';
3
2
  /**
4
3
  * useTabActions - 标签操作对外 Hook
5
4
  *
@@ -14,55 +13,15 @@ import { ITabData, IframeRefreshMode } from '../model/TabModel';
14
13
  * - reset() 重置(关闭所有)
15
14
  */
16
15
  export default function useTabActions(): {
17
- openTab: throttle<(tab: ITabData, renew?: any) => Promise<string | undefined>>;
16
+ openTab: (tab: ITabData, renew?: boolean) => Promise<string | undefined>;
18
17
  closeTab: (id: string) => string;
19
18
  closeAllTabs: () => void;
20
19
  refreshTab: (id: string) => void;
21
20
  refreshAllTabs: () => void;
22
- activeTab: (id: string, route?: boolean) => void;
21
+ activeTab: (id: string, route?: boolean) => Promise<void | import('vue-router').NavigationFailure | undefined>;
23
22
  reset: () => void;
24
- tabs: import('vue').Ref<{
25
- id: string;
26
- closable: boolean;
27
- refreshable: boolean;
28
- iframe: boolean;
29
- iframeRefreshMode?: IframeRefreshMode | undefined;
30
- url?: string | undefined;
31
- active: boolean;
32
- pages: {
33
- push: (element: import('..').ITabPage) => void;
34
- pop: () => import('..').ITabPage | undefined;
35
- peek: () => import('..').ITabPage | undefined;
36
- isEmpty: () => boolean;
37
- size: () => number;
38
- clear: () => void;
39
- list: () => import('..').ITabPage[];
40
- toString: () => string;
41
- toJSON: () => import('..').ITabPage[];
42
- };
43
- title: string;
44
- }[], import('..').ITabItem[] | {
45
- id: string;
46
- closable: boolean;
47
- refreshable: boolean;
48
- iframe: boolean;
49
- iframeRefreshMode?: IframeRefreshMode | undefined;
50
- url?: string | undefined;
51
- active: boolean;
52
- pages: {
53
- push: (element: import('..').ITabPage) => void;
54
- pop: () => import('..').ITabPage | undefined;
55
- peek: () => import('..').ITabPage | undefined;
56
- isEmpty: () => boolean;
57
- size: () => number;
58
- clear: () => void;
59
- list: () => import('..').ITabPage[];
60
- toString: () => string;
61
- toJSON: () => import('..').ITabPage[];
62
- };
63
- title: string;
64
- }[]>;
23
+ tabs: import('vue').Ref<import('..').ITabItem[], import('..').ITabItem[]>;
65
24
  setIFramePath: (path: string) => void;
66
- getWrapper: () => Element | undefined;
25
+ getWrapper: () => Element;
67
26
  openInNewWindow: (id: string) => void;
68
27
  };
@@ -1,18 +1,31 @@
1
+ import { InjectionKey } from 'vue';
1
2
  import { Emitter, EventType } from 'mitt';
2
- import { App } from 'vue';
3
3
  declare module '@vue/runtime-core' {
4
4
  interface ComponentCustomProperties {
5
- $layerEmitter: Emitter<Record<EventType, unknown>>;
5
+ $layerEmitter: Emitter<TabEventPayloadMap>;
6
6
  }
7
7
  }
8
- declare const plugin: {
9
- install(app: App): void;
10
- };
11
- /** 获取标签事件总线,用于组件间通信 */
12
- export declare const useTabEmitter: <T extends Record<EventType, unknown>>() => Emitter<T>;
13
8
  /** 标签相关事件类型 */
14
9
  export declare enum TabEventType {
15
10
  PAGE_LOADING = "PAGE_LOADING",
16
- TAB_ACTIVE = "TAB_ACTIVE"
11
+ TAB_ACTIVE = "TAB_ACTIVE",
12
+ FORWARD = "FORWARD",
13
+ BACKWARD = "BACKWARD",
14
+ REFRESH_IFRAME_POSTMESSAGE = "REFRESH_IFRAME_POSTMESSAGE"
15
+ }
16
+ export interface TabEventPayloadMap extends Record<EventType, unknown> {
17
+ [TabEventType.PAGE_LOADING]: {
18
+ tId: string;
19
+ value: boolean;
20
+ };
21
+ [TabEventType.TAB_ACTIVE]: {
22
+ id: string;
23
+ isRoute?: boolean;
24
+ };
25
+ [TabEventType.FORWARD]: void;
26
+ [TabEventType.BACKWARD]: void;
27
+ [TabEventType.REFRESH_IFRAME_POSTMESSAGE]: string;
17
28
  }
18
- export default plugin;
29
+ export declare const tabEmitterKey: InjectionKey<Emitter<TabEventPayloadMap>>;
30
+ /** 获取标签事件总线,用于组件间通信 */
31
+ export declare const useTabEmitter: () => Emitter<TabEventPayloadMap>;