vue-stack-tabs 2.2.0 → 2.4.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 (57) hide show
  1. package/README.en.md +14 -0
  2. package/README.md +14 -0
  3. package/dist/StackTabs.vue.d.ts +2 -2
  4. package/dist/components/StackKeepAlive/StackCacheRenderer.vue.d.ts +0 -2
  5. package/dist/hooks/stackTabsContext.d.ts +0 -1
  6. package/dist/hooks/tabPageContext.d.ts +18 -0
  7. package/dist/hooks/tabPanel/evict.d.ts +2 -2
  8. package/dist/hooks/tabPanel/scroll.d.ts +0 -2
  9. package/dist/hooks/tabPanel/state.d.ts +0 -1
  10. package/dist/hooks/useIframeManager.d.ts +1 -1
  11. package/dist/hooks/useScrollShadow.d.ts +11 -2
  12. package/dist/hooks/useTabEventBus.d.ts +5 -0
  13. package/dist/hooks/useTabLoading.d.ts +2 -1
  14. package/dist/hooks/useTabRouter.d.ts +5 -1
  15. package/dist/model/TabModel.d.ts +12 -5
  16. package/dist/nuxt/module.mjs +1 -1
  17. package/dist/nuxt/runtime/plugin.mjs +8 -1598
  18. package/dist/style.css +1 -1
  19. package/dist/utils/scrollUtils.d.ts +0 -2
  20. package/dist/utils/tabInfoEncoder.d.ts +0 -2
  21. package/dist/utils/urlParser.d.ts +6 -7
  22. package/dist/vue-stack-tabs.es.js +1 -1
  23. package/package.json +2 -2
  24. package/src/lib/StackTabs.vue +16 -1
  25. package/src/lib/assets/image/open.svg +1 -1
  26. package/src/lib/assets/style/common/mixins.scss +6 -6
  27. package/src/lib/assets/style/common/theme.scss +148 -0
  28. package/src/lib/assets/style/common/var.scss +104 -1
  29. package/src/lib/assets/style/index.scss +1 -0
  30. package/src/lib/assets/style/loading.scss +3 -3
  31. package/src/lib/assets/style/stack-tab.scss +62 -56
  32. package/src/lib/components/ContextMenu/index.vue +3 -1
  33. package/src/lib/components/PageLoading.vue +10 -9
  34. package/src/lib/components/StackKeepAlive/StackCacheRenderer.vue +1 -5
  35. package/src/lib/components/TabHeader/TabHeaderScroll.vue +164 -90
  36. package/src/lib/components/TabHeader/index.vue +51 -10
  37. package/src/lib/hooks/stackTabsContext.ts +0 -1
  38. package/src/lib/hooks/tabPageContext.ts +50 -0
  39. package/src/lib/hooks/tabPanel/evict.ts +14 -6
  40. package/src/lib/hooks/tabPanel/pageComponentFactory.tsx +224 -47
  41. package/src/lib/hooks/tabPanel/scroll.ts +4 -15
  42. package/src/lib/hooks/tabPanel/session.ts +11 -4
  43. package/src/lib/hooks/tabPanel/state.ts +0 -2
  44. package/src/lib/hooks/tabPanel/tabStore.ts +28 -10
  45. package/src/lib/hooks/useIframeManager.ts +11 -15
  46. package/src/lib/hooks/useScrollShadow.ts +74 -19
  47. package/src/lib/hooks/useTabActions.ts +10 -4
  48. package/src/lib/hooks/useTabEventBus.ts +3 -0
  49. package/src/lib/hooks/useTabLoading.ts +6 -4
  50. package/src/lib/hooks/useTabPanel.tsx +46 -34
  51. package/src/lib/hooks/useTabRouter.ts +11 -14
  52. package/src/lib/model/TabModel.ts +14 -5
  53. package/src/lib/nuxt/module.ts +3 -1
  54. package/src/lib/nuxt/runtime/plugin.ts +7 -3
  55. package/src/lib/utils/scrollUtils.ts +0 -18
  56. package/src/lib/utils/tabInfoEncoder.ts +21 -2
  57. package/src/lib/utils/urlParser.ts +40 -37
@@ -44,15 +44,6 @@ export function isAllowedTabUrl(url: string): boolean {
44
44
  }
45
45
  }
46
46
 
47
- /**
48
- * 判断是否为不允许嵌入 iframe 的 URL
49
- * @param url - 待检查的 URL 字符串
50
- * @returns 非 allowlist URL 返回 true
51
- */
52
- export function isInvalidIframeUrl(url: string): boolean {
53
- return !isAllowedTabUrl(url)
54
- }
55
-
56
47
  /** 非法 URL 降级为 about:blank,避免进入 iframe/window.open 等 URL sink */
57
48
  export function toSafeTabUrl(url: string): string {
58
49
  return isAllowedTabUrl(url) ? url : 'about:blank'
@@ -152,39 +143,51 @@ export function clonePage<T extends CloneablePage>(page: T): T {
152
143
  }
153
144
  }
154
145
 
155
- /** 忽略 __tab/__src/_back 等内部保留 key 后比较两个 query 是否等价 */
146
+ /** 判断 key 是否为 vue-stack-tabs 内部保留参数(含后退参数 _back) */
147
+ const isIgnoredQueryKey = (key: string): boolean =>
148
+ STACK_TABS_RESERVED_QUERY_KEYS.has(key) || key === '_back'
149
+
150
+ /** 比较两个 query 值:数组按元素逐个比较,标量按字符串比较,并区分数组与标量 */
151
+ const isSameQueryValue = (a: LocationQueryRaw[string], b: LocationQueryRaw[string]): boolean => {
152
+ const isArrayA = Array.isArray(a)
153
+ const isArrayB = Array.isArray(b)
154
+ if (isArrayA || isArrayB) {
155
+ if (!isArrayA || !isArrayB || a.length !== b.length) return false
156
+ for (let index = 0; index < a.length; index++) {
157
+ const itemA = a[index]
158
+ const itemB = b[index]
159
+ if (itemA !== itemB && String(itemA ?? '') !== String(itemB ?? '')) return false
160
+ }
161
+ return true
162
+ }
163
+ // 绝大多数 query 值是字符串:先做值直比,避免每次都 String() 分配。
164
+ return a === b || String(a ?? '') === String(b ?? '')
165
+ }
166
+
167
+ /**
168
+ * 忽略 __tab/__src/_back 等内部保留 key 后比较两个 query 是否等价。
169
+ *
170
+ * 直接按键逐一比较,不构造中间对象;同时要求 key 集合完全一致
171
+ * (旧实现只比较 key 数量,`{ a: '' }` 与 `{ b: '' }` 会被误判为相等)。
172
+ */
156
173
  export function isSameQueryIgnoringReserved(
157
174
  a: LocationQueryRaw = {},
158
175
  b: LocationQueryRaw = {}
159
176
  ): boolean {
160
- const clean = (q: LocationQueryRaw): Record<string, string | string[]> => {
161
- const result: Record<string, string | string[]> = {}
162
- for (const [key, value] of Object.entries(q)) {
163
- if (STACK_TABS_RESERVED_QUERY_KEYS.has(key) || key === '_back') continue
164
- result[key] = Array.isArray(value)
165
- ? value.map((item) => String(item ?? ''))
166
- : String(value ?? '')
167
- }
168
- return result
177
+ let countA = 0
178
+ for (const key of Object.keys(a)) {
179
+ if (isIgnoredQueryKey(key)) continue
180
+ countA++
181
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false
182
+ if (!isSameQueryValue(a[key], b[key])) return false
169
183
  }
170
- const ca = clean(a)
171
- const cb = clean(b)
172
- const keysA = Object.keys(ca)
173
- const keysB = Object.keys(cb)
174
- if (keysA.length !== keysB.length) return false
175
- return keysA.every((key) => {
176
- const aValue = ca[key]
177
- const bValue = cb[key]
178
- if (Array.isArray(aValue) || Array.isArray(bValue)) {
179
- return (
180
- Array.isArray(aValue) &&
181
- Array.isArray(bValue) &&
182
- aValue.length === bValue.length &&
183
- aValue.every((value, index) => value === bValue[index])
184
- )
185
- }
186
- return aValue === bValue
187
- })
184
+
185
+ let countB = 0
186
+ for (const key of Object.keys(b)) {
187
+ if (isIgnoredQueryKey(key)) continue
188
+ countB++
189
+ }
190
+ return countA === countB
188
191
  }
189
192
 
190
193
  /** 移除 query 中的 vue-stack-tabs 保留参数(__tab、__src),返回干净的 query */