vue-stack-tabs 0.1.45 → 0.1.47

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.
@@ -1,119 +1,119 @@
1
- import { useRouter } from 'vue-router'
2
- import useTabpanel from '../hooks/useTabpanel'
3
- import { throttle } from 'throttle-debounce'
4
- import type { ITabData } from '../model/TabModel'
5
- import { defu } from 'defu'
6
-
7
- import { encodeTabInfo } from '../utils/TabIdHelper'
8
- import { uriDecode } from '../utils/UriHelper'
9
- import { ulid } from 'ulid'
10
- import { MittType, useEmitter } from './useTabMitt'
11
- import { nextTick } from 'vue'
12
-
13
- let iframePath: string
14
- export default () => {
15
- const router = useRouter()
16
- const { active, hasTab, pageShown, reset, canAddTab, renewTab, getTab } = useTabpanel()
17
- const emitter = useEmitter()
18
- /**
19
- * 打开新的TAB页面
20
- * @param tab
21
- * @param to
22
- */
23
- const openNewTab = throttle(500, (tab: ITabData, renew = false) => {
24
- return new Promise((resolve, reject) => {
25
- if (!pageShown.value) {
26
- reject()
27
- return
28
- }
29
- const tabInfo = defu(tab, {
30
- refreshable: true,
31
- closable: true,
32
- iframe: false
33
- })
34
- if (tabInfo.id && renew && hasTab(tabInfo.id!)) {
35
- renewTab(tab)
36
- const currentTab = getTab(tab.id!)
37
- // if current tab is active, then must reload the component of router view for complete remove the cache!!!
38
- if (currentTab?.active) {
39
- pageShown.value = false
40
- nextTick(() => {
41
- pageShown.value = true
42
- })
43
- }
44
- }
45
- /**
46
- * In the function of openNewTab, if the tab already 'ACTIVE' then do nothing.
47
- */
48
- if (hasTab(tabInfo.id!) && !renew) {
49
- emitter.emit(MittType.TAB_ACTIVE, { id: tabInfo.id! })
50
- // active(tabInfo.id!)
51
- resolve(tabInfo.id)
52
- return
53
- }
54
- if (!tabInfo.id) {
55
- tabInfo.id = ulid()
56
- }
57
- if (!canAddTab()) {
58
- reject('Max Size')
59
- return
60
- }
61
-
62
- // if (initialed) {
63
- // // 增加tab
64
- // let activeTab: ITabItem | null = null
65
- // for (const tab of unref(tabs)) {
66
- // tab.active = false
67
- // }
68
- // activeTab = {
69
- // id: tabInfo.id!,
70
- // title: tabInfo.title,
71
- // closable: tabInfo.closable!,
72
- // refreshable: tabInfo.refreshable!,
73
- // iframe: tabInfo.iframe!,
74
- // url: tabInfo.iframe ? tab.path : null,
75
- // active: true,
76
- // pages: new Stack<ITabPage>()
77
- // } as ITabItem
78
- // addTab(activeTab).finally(() => {
79
- // toPage(tab, tabInfo)
80
- // resolve(tabInfo.id)
81
- // })
82
- // } else {
83
- toPage(tab, tabInfo)
84
- resolve(tabInfo.id)
85
- // }
86
- })
87
- })
88
- const toPage = (tab: ITabData, tabInfo: any) => {
89
- const __tab = encodeTabInfo(tabInfo)
90
- let query = defu(
91
- {
92
- __tab
93
- },
94
- tab.query
95
- ) as Record<string, string>
96
- let path
97
- if (!tab.iframe) {
98
- const url = uriDecode(tab.path)
99
- path = url.path
100
- query = defu(url.query, query)
101
- } else {
102
- const __src = encodeURIComponent(tab.path as string)
103
- path = iframePath
104
- query['__src'] = __src
105
- }
106
- router.push({
107
- path,
108
- query
109
- })
110
- }
111
- const setIFramePath = (path: string) => {
112
- iframePath = path
113
- }
114
- const getWrapper = () =>{
115
- return document.getElementsByClassName('cache-page-wrapper')[0]
116
- }
117
-
118
- return { openNewTab, setIFramePath, active, reset,getWrapper }
119
- }
1
+ import { useRouter } from 'vue-router'
2
+ import useTabpanel from '../hooks/useTabpanel'
3
+ import { throttle } from 'throttle-debounce'
4
+ import type { ITabData } from '../model/TabModel'
5
+ import { defu } from 'defu'
6
+
7
+ import { encodeTabInfo } from '../utils/TabIdHelper'
8
+ import { uriDecode } from '../utils/UriHelper'
9
+ import { ulid } from 'ulid'
10
+ import { MittType, useEmitter } from './useTabMitt'
11
+ import { nextTick } from 'vue'
12
+
13
+ let iframePath: string
14
+ export default () => {
15
+ const router = useRouter()
16
+ const { active, hasTab, pageShown, reset, canAddTab, renewTab, getTab } = useTabpanel()
17
+ const emitter = useEmitter()
18
+ /**
19
+ * 打开新的TAB页面
20
+ * @param tab
21
+ * @param to
22
+ */
23
+ const openNewTab = throttle(500, (tab: ITabData, renew = false) => {
24
+ return new Promise((resolve, reject) => {
25
+ if (!pageShown.value) {
26
+ reject()
27
+ return
28
+ }
29
+ const tabInfo = defu(tab, {
30
+ refreshable: true,
31
+ closable: true,
32
+ iframe: false
33
+ })
34
+ if (tabInfo.id && renew && hasTab(tabInfo.id!)) {
35
+ renewTab(tab)
36
+ const currentTab = getTab(tab.id!)
37
+ // if current tab is active, then must reload the component of router view for complete remove the cache!!!
38
+ if (currentTab?.active) {
39
+ pageShown.value = false
40
+ nextTick(() => {
41
+ pageShown.value = true
42
+ })
43
+ }
44
+ }
45
+ /**
46
+ * In the function of openNewTab, if the tab already 'ACTIVE' then do nothing.
47
+ */
48
+ if (hasTab(tabInfo.id!) && !renew) {
49
+ emitter.emit(MittType.TAB_ACTIVE, { id: tabInfo.id! })
50
+ // active(tabInfo.id!)
51
+ resolve(tabInfo.id)
52
+ return
53
+ }
54
+ if (!tabInfo.id) {
55
+ tabInfo.id = ulid()
56
+ }
57
+ if (!canAddTab()) {
58
+ reject('Max Size')
59
+ return
60
+ }
61
+
62
+ // if (initialed) {
63
+ // // 增加tab
64
+ // let activeTab: ITabItem | null = null
65
+ // for (const tab of unref(tabs)) {
66
+ // tab.active = false
67
+ // }
68
+ // activeTab = {
69
+ // id: tabInfo.id!,
70
+ // title: tabInfo.title,
71
+ // closable: tabInfo.closable!,
72
+ // refreshable: tabInfo.refreshable!,
73
+ // iframe: tabInfo.iframe!,
74
+ // url: tabInfo.iframe ? tab.path : null,
75
+ // active: true,
76
+ // pages: new Stack<ITabPage>()
77
+ // } as ITabItem
78
+ // addTab(activeTab).finally(() => {
79
+ // toPage(tab, tabInfo)
80
+ // resolve(tabInfo.id)
81
+ // })
82
+ // } else {
83
+ toPage(tab, tabInfo)
84
+ resolve(tabInfo.id)
85
+ // }
86
+ })
87
+ })
88
+ const toPage = (tab: ITabData, tabInfo: any) => {
89
+ const __tab = encodeTabInfo(tabInfo)
90
+ let query = defu(
91
+ {
92
+ __tab
93
+ },
94
+ tab.query
95
+ ) as Record<string, string>
96
+ let path
97
+ if (!tab.iframe) {
98
+ const url = uriDecode(tab.path)
99
+ path = url.path
100
+ query = defu(url.query, query)
101
+ } else {
102
+ const __src = encodeURIComponent(tab.path as string)
103
+ path = iframePath
104
+ query['__src'] = __src
105
+ }
106
+ router.push({
107
+ path,
108
+ query
109
+ })
110
+ }
111
+ const setIFramePath = (path: string) => {
112
+ iframePath = path
113
+ }
114
+ const getWrapper = () =>{
115
+ return document.getElementsByClassName('cache-page-wrapper')[0]
116
+ }
117
+
118
+ return { openNewTab, setIFramePath, active, reset,getWrapper }
119
+ }
@@ -1,103 +1,103 @@
1
- import { defu } from 'defu'
2
-
3
- import { getCurrentInstance } from 'vue'
4
- import type { ComponentInternalInstance } from 'vue'
5
- import { type RouteLocationPathRaw, useRoute, useRouter } from 'vue-router'
6
-
7
- import useTabpanel from '@/lib/hooks/useTabpanel'
8
- import { uriDecode } from '@/lib/utils/UriHelper'
9
- import { useEmitter } from '@/lib/hooks/useTabMitt'
10
- export default () => {
11
- const { attrs, props } = getCurrentInstance() as ComponentInternalInstance
12
- const { pageShown, getTab, markDeletableCache, removeComponent, getComponent, addPageScroller } =
13
- useTabpanel()
14
- const route = useRoute()
15
- const router = useRouter()
16
- const emitter = useEmitter()
17
- let currentId = ''
18
- // const currentPageId = ''
19
- let currentTab = ''
20
- currentId = (attrs.tId || props.tId) as string
21
- const currentPageId = (attrs.pId || props.pId) as string
22
- // currentPageId = attrs.pId as string
23
- currentTab = route.query.__tab as string
24
-
25
- const addScroller = (...scroller: string[]) => {
26
- addPageScroller(currentPageId, ...scroller)
27
- }
28
- const forward = (to: RouteLocationPathRaw) => {
29
- // pageShown.value = false
30
- const query = defu(
31
- {
32
- __tab: currentTab
33
- },
34
- to.query
35
- )
36
- emitter.emit('FORWARD')
37
- router.push({
38
- path: to.path,
39
- query
40
- })
41
- }
42
- /**
43
- * 返回
44
- * @param to url or step
45
- * @param query 返回时要加截的数据
46
- */
47
- const backward = (to: number | string, query?: Object) => {
48
- // pageShown.value = false
49
-
50
- const tab = getTab(currentId)
51
- if (!tab) {
52
- return
53
- }
54
- const tabStack = tab.pages
55
- let absTo = 0
56
- if (typeof to === 'number') {
57
- absTo = Math.abs(to)
58
- } else {
59
- let index = 1
60
- let hasPath = false
61
- const pageItems = tabStack.list()
62
- const url = uriDecode(to)
63
- query = defu(url.query, query)
64
- for (let i = tabStack.size() - 2; i >= 0; i--) {
65
- if (pageItems[i].path === url.path) {
66
- hasPath = true
67
- break
68
- }
69
- index += 1
70
- }
71
- if (hasPath) {
72
- absTo = index
73
- }
74
- }
75
-
76
- if (absTo === 0 || tabStack!.size() <= absTo) {
77
- return
78
- }
79
- // const removeCaches = new Set()
80
- for (let i = 0; i < absTo; i++) {
81
- const popRoute = tabStack.pop()
82
- // removeComponent(popRoute!.id)
83
- markDeletableCache(popRoute!.id)
84
- }
85
- const pageRoute = tabStack?.peek()
86
- const component = getComponent(pageRoute!.id)
87
- if (component !== null && component !== undefined) {
88
- const sourceComponent = component.components.DynamicComponent
89
- sourceComponent.props = defu(query, sourceComponent.props)
90
- }
91
- emitter.emit('BACKWARD')
92
- router.push({
93
- path: pageRoute!.path,
94
- query: pageRoute!.query
95
- })
96
- }
97
-
98
- return {
99
- forward,
100
- backward,
101
- addScroller
102
- }
103
- }
1
+ import { defu } from 'defu'
2
+
3
+ import { getCurrentInstance } from 'vue'
4
+ import type { ComponentInternalInstance } from 'vue'
5
+ import { type RouteLocationPathRaw, useRoute, useRouter } from 'vue-router'
6
+
7
+ import useTabpanel from '@/lib/hooks/useTabpanel'
8
+ import { uriDecode } from '@/lib/utils/UriHelper'
9
+ import { useEmitter } from '@/lib/hooks/useTabMitt'
10
+ export default () => {
11
+ const { attrs, props } = getCurrentInstance() as ComponentInternalInstance
12
+ const { pageShown, getTab, markDeletableCache, removeComponent, getComponent, addPageScroller } =
13
+ useTabpanel()
14
+ const route = useRoute()
15
+ const router = useRouter()
16
+ const emitter = useEmitter()
17
+ let currentId = ''
18
+ // const currentPageId = ''
19
+ let currentTab = ''
20
+ currentId = (attrs.tId || props.tId) as string
21
+ const currentPageId = (attrs.pId || props.pId) as string
22
+ // currentPageId = attrs.pId as string
23
+ currentTab = route.query.__tab as string
24
+
25
+ const addScroller = (...scroller: string[]) => {
26
+ addPageScroller(currentPageId, ...scroller)
27
+ }
28
+ const forward = (to: RouteLocationPathRaw) => {
29
+ // pageShown.value = false
30
+ const query = defu(
31
+ {
32
+ __tab: currentTab
33
+ },
34
+ to.query
35
+ )
36
+ emitter.emit('FORWARD')
37
+ router.push({
38
+ path: to.path,
39
+ query
40
+ })
41
+ }
42
+ /**
43
+ * 返回
44
+ * @param to url or step
45
+ * @param query 返回时要加截的数据
46
+ */
47
+ const backward = (to: number | string, query?: Object) => {
48
+ // pageShown.value = false
49
+
50
+ const tab = getTab(currentId)
51
+ if (!tab) {
52
+ return
53
+ }
54
+ const tabStack = tab.pages
55
+ let absTo = 0
56
+ if (typeof to === 'number') {
57
+ absTo = Math.abs(to)
58
+ } else {
59
+ let index = 1
60
+ let hasPath = false
61
+ const pageItems = tabStack.list()
62
+ const url = uriDecode(to)
63
+ query = defu(url.query, query)
64
+ for (let i = tabStack.size() - 2; i >= 0; i--) {
65
+ if (pageItems[i].path === url.path) {
66
+ hasPath = true
67
+ break
68
+ }
69
+ index += 1
70
+ }
71
+ if (hasPath) {
72
+ absTo = index
73
+ }
74
+ }
75
+
76
+ if (absTo === 0 || tabStack!.size() <= absTo) {
77
+ return
78
+ }
79
+ // const removeCaches = new Set()
80
+ for (let i = 0; i < absTo; i++) {
81
+ const popRoute = tabStack.pop()
82
+ // removeComponent(popRoute!.id)
83
+ markDeletableCache(popRoute!.id)
84
+ }
85
+ const pageRoute = tabStack?.peek()
86
+ const component = getComponent(pageRoute!.id)
87
+ if (component !== null && component !== undefined) {
88
+ const sourceComponent = component.components.DynamicComponent
89
+ sourceComponent.props = defu(query, sourceComponent.props)
90
+ }
91
+ emitter.emit('BACKWARD')
92
+ router.push({
93
+ path: pageRoute!.path,
94
+ query: pageRoute!.query
95
+ })
96
+ }
97
+
98
+ return {
99
+ forward,
100
+ backward,
101
+ addScroller
102
+ }
103
+ }
@@ -27,8 +27,8 @@ const defaultTabs: ITabItem[] = []
27
27
  const caches = ref<string[]>([])
28
28
  // Dynamic components
29
29
  const components = new Map<string, any>()
30
- const deletableCache = new Set<String>()
31
- const deletableTab = new Set<String>()
30
+ const deletableCache = new Set<string>()
31
+ const deletableTab = new Set<string>()
32
32
  const pageShown = ref<boolean>(true)
33
33
  const SESSION_TAB_NAME = 'stacktab-active-tab'
34
34
  const pageScroller = new Map<string, Map<string, any>>()
@@ -563,13 +563,19 @@ export default () => {
563
563
  deletableCache.add(cacheName)
564
564
  }
565
565
  const removeDeletableCache = () => {
566
- for (let i = caches.value.length - 1; i >= 0; i--) {
567
- if (deletableCache.has(caches.value[i])) {
568
- unref(caches).splice(i, 1)
569
- removeComponent(caches.value[i])
566
+ if(deletableCache.size>0){
567
+ for (let i = caches.value.length - 1; i >= 0; i--) {
568
+ if (deletableCache.has(caches.value[i])) {
569
+ unref(caches).splice(i, 1)
570
+
571
+ }
570
572
  }
573
+ for(const cacheName of deletableCache) {
574
+ removeComponent(cacheName)
575
+ }
576
+ deletableCache.clear()
571
577
  }
572
- deletableCache.clear()
578
+
573
579
  }
574
580
  const removeCache = (cacheName: string) => {
575
581
  for (let i = caches.value.length - 1; i >= 0; i--) {
@@ -1,70 +1,70 @@
1
- import type { ITabBase } from '../model/TabModel'
2
- import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from 'lz-string'
3
- import Hex from 'hex-encoding'
4
- import { blake3 } from '@noble/hashes/blake3'
5
-
6
- /**
7
- * compress tab info to string
8
- * format:
9
- * {tab id}{current timestamp}{tab name}{tab closable}
10
- * @param tabData
11
- */
12
- export const encodeTabInfo = (tabData: ITabBase): string => {
13
- // const info: ITabBase = {
14
- // id: tabData.id,
15
- // title: tabData.title,
16
- // iframe: tabData.iframe,
17
- // closable: tabData.closable,
18
- // refreshable: tabData.refreshable
19
- // }
20
- const info = `${tabData.id}|${tabData.title}|${tabData.iframe ? 'Y' : 'N'}|${tabData.closable ? 'Y' : 'N'}|${tabData.refreshable ? 'Y' : 'N'}`
21
- return compressToEncodedURIComponent(info)
22
- // const compressed = await compress(new TextEncoder().encode(info))
23
- // return fromUint8Array(compressed, true)
24
- // return compressToEncodedURIComponent(
25
- // `${tabData.id}|${tabData.title}|${tabData.iframe ? 'Y' : 'N'}|${tabData.closable ? 'Y' : 'N'}|${tabData.refreshable ? 'Y' : 'N'}`
26
- // )
27
- }
28
-
29
- export const decodeTabInfo = (tab: string): ITabBase => {
30
- // const decompressed = await decompress(toUint8Array(tab))
31
- const tabString = decompressFromEncodedURIComponent(tab)
32
- // const tabString = new TextDecoder().decode(decompressed)
33
- // return JSON.parse(new TextDecoder().decode(decompressed))
34
- // const tabString = decompressFromEncodedURIComponent(tab)
35
- const tabValues: string[] = tabString.split('|')
36
- return {
37
- id: tabValues[0],
38
- title: tabValues[1],
39
- iframe: tabValues[2] === 'Y',
40
- closable: tabValues[3] === 'Y',
41
- refreshable: tabValues[4] === 'Y'
42
- }
43
- }
44
-
45
- export const createPageId = (tabId: string, path: string, query: Object): string => {
46
- const queryArray = []
47
- console.log(query)
48
- type ObjectKey = keyof typeof query
49
- for (const key in query) {
50
- if (!key.startsWith('__')) {
51
- queryArray.push({
52
- key,
53
- value: query[key as ObjectKey]
54
- })
55
- }
56
- }
57
- queryArray.sort((a, b) => {
58
- let result = 0
59
- if (a.key > b.key) {
60
- result = 1
61
- } else if (a.key < b.key) {
62
- result = -1
63
- }
64
- return result
65
- })
66
- const hash = blake3(`${tabId}|${path}|${JSON.stringify(queryArray)}`)
67
- return Hex.encode(hash)
68
- // return uuidv5(`${tabId}|${path}|${JSON.stringify(queryArray)}`, uuidv5.URL).replaceAll('-', '')
69
- // return await blake3(`${tabId}|${path}|${JSON.stringify(queryArray)}`)
70
- }
1
+ import type { ITabBase } from '../model/TabModel'
2
+ import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from 'lz-string'
3
+ import Hex from 'hex-encoding'
4
+ import { blake3 } from '@noble/hashes/blake3'
5
+
6
+ /**
7
+ * compress tab info to string
8
+ * format:
9
+ * {tab id}{current timestamp}{tab name}{tab closable}
10
+ * @param tabData
11
+ */
12
+ export const encodeTabInfo = (tabData: ITabBase): string => {
13
+ // const info: ITabBase = {
14
+ // id: tabData.id,
15
+ // title: tabData.title,
16
+ // iframe: tabData.iframe,
17
+ // closable: tabData.closable,
18
+ // refreshable: tabData.refreshable
19
+ // }
20
+ const info = `${tabData.id}|${tabData.title}|${tabData.iframe ? 'Y' : 'N'}|${tabData.closable ? 'Y' : 'N'}|${tabData.refreshable ? 'Y' : 'N'}`
21
+ return compressToEncodedURIComponent(info)
22
+ // const compressed = await compress(new TextEncoder().encode(info))
23
+ // return fromUint8Array(compressed, true)
24
+ // return compressToEncodedURIComponent(
25
+ // `${tabData.id}|${tabData.title}|${tabData.iframe ? 'Y' : 'N'}|${tabData.closable ? 'Y' : 'N'}|${tabData.refreshable ? 'Y' : 'N'}`
26
+ // )
27
+ }
28
+
29
+ export const decodeTabInfo = (tab: string): ITabBase => {
30
+ // const decompressed = await decompress(toUint8Array(tab))
31
+ const tabString = decompressFromEncodedURIComponent(tab)
32
+ // const tabString = new TextDecoder().decode(decompressed)
33
+ // return JSON.parse(new TextDecoder().decode(decompressed))
34
+ // const tabString = decompressFromEncodedURIComponent(tab)
35
+ const tabValues: string[] = tabString.split('|')
36
+ return {
37
+ id: tabValues[0],
38
+ title: tabValues[1],
39
+ iframe: tabValues[2] === 'Y',
40
+ closable: tabValues[3] === 'Y',
41
+ refreshable: tabValues[4] === 'Y'
42
+ }
43
+ }
44
+
45
+ export const createPageId = (tabId: string, path: string, query: Object): string => {
46
+ const queryArray = []
47
+ console.log(query)
48
+ type ObjectKey = keyof typeof query
49
+ for (const key in query) {
50
+ if (!key.startsWith('__')) {
51
+ queryArray.push({
52
+ key,
53
+ value: query[key as ObjectKey]
54
+ })
55
+ }
56
+ }
57
+ queryArray.sort((a, b) => {
58
+ let result = 0
59
+ if (a.key > b.key) {
60
+ result = 1
61
+ } else if (a.key < b.key) {
62
+ result = -1
63
+ }
64
+ return result
65
+ })
66
+ const hash = blake3(`${tabId}|${path}|${JSON.stringify(queryArray)}`)
67
+ return Hex.encode(hash)
68
+ // return uuidv5(`${tabId}|${path}|${JSON.stringify(queryArray)}`, uuidv5.URL).replaceAll('-', '')
69
+ // return await blake3(`${tabId}|${path}|${JSON.stringify(queryArray)}`)
70
+ }