@tnotesjs/core 0.1.28 → 0.2.1
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.
- package/README.md +2 -1
- package/dist/{chunk-4KG6RXFS.js → chunk-5QN44ES5.js} +2946 -1533
- package/dist/{chunk-AGSL3VZE.js → chunk-5Y5IYS6C.js} +7 -0
- package/dist/cli/index.js +343 -40
- package/dist/index.js +1 -1
- package/dist/vitepress/config/index.js +573 -65
- package/package.json +4 -1
- package/templates/sub-repo/.gitattributes +30 -0
- package/templates/sub-repo/.github/workflows/deploy.yml +87 -0
- package/templates/sub-repo/.vitepress/config.mts +12 -0
- package/templates/sub-repo/.vitepress/env.d.ts +10 -0
- package/templates/sub-repo/.vitepress/theme/index.ts +12 -0
- package/templates/sub-repo/.vscode/settings.json +330 -0
- package/templates/sub-repo/.vscode/tnotes.code-snippets +110 -0
- package/templates/sub-repo/public/favicon.ico +0 -0
- package/templates/sub-repo/public/logo.png +0 -0
- package/templates/sub-repo/tsconfig.json +45 -0
- package/vitepress/assets/icons/icon__setting_dark.svg +4 -0
- package/vitepress/assets/icons/icon__setting_light.svg +4 -0
- package/vitepress/assets/icons/index.ts +2 -2
- package/vitepress/client/localSearchIndexBridge.ts +24 -0
- package/vitepress/components/Discussions/Discussions.vue +1 -1
- package/vitepress/components/Footprints/Footprints.vue +90 -25
- package/vitepress/components/Layout/CustomSidebar.vue +489 -117
- package/vitepress/components/Layout/Layout.vue +5 -0
- package/vitepress/components/Layout/NavBarSettingsTrigger.vue +10 -2
- package/vitepress/components/Layout/SidebarItems.vue +517 -342
- package/vitepress/components/Layout/SidebarNavBefore.vue +1 -50
- package/vitepress/components/Layout/SidebarResizeHandle.vue +6 -23
- package/vitepress/components/Layout/composables/docLayoutLogic.ts +59 -0
- package/vitepress/components/Layout/composables/useDocLayout.test.ts +69 -0
- package/vitepress/components/Layout/composables/useDocLayout.ts +128 -0
- package/vitepress/components/Layout/composables/useSidebarDrag.ts +514 -0
- package/vitepress/components/Layout/composables/useSidebarLayout.ts +25 -8
- package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +8 -3
- package/vitepress/components/Layout/sidebarCollapsedState.test.ts +183 -0
- package/vitepress/components/Layout/sidebarCollapsedState.ts +241 -0
- package/vitepress/components/Layout/sidebarDragLogic.test.ts +1146 -0
- package/vitepress/components/Layout/sidebarDragLogic.ts +1307 -0
- package/vitepress/components/Layout/sidebarHitTest.test.ts +572 -0
- package/vitepress/components/Layout/sidebarHitTest.ts +801 -0
- package/vitepress/components/Layout/sidebarItemRenderLogic.test.ts +53 -0
- package/vitepress/components/Layout/sidebarItemRenderLogic.ts +50 -0
- package/vitepress/components/MarkMap/MarkMap.vue +67 -10
- package/vitepress/components/Settings/Settings.vue +78 -74
- package/vitepress/components/SidebarCard/FolderTreeItems.vue +268 -0
- package/vitepress/components/SidebarCard/SidebarCard.vue +125 -274
- package/vitepress/components/constants.ts +18 -6
- package/vitepress/components/sidebar.data.ts +13 -11
- package/vitepress/components/sidebarTreeHelpers.test.ts +142 -0
- package/vitepress/components/sidebarTreeHelpers.ts +168 -0
- package/vitepress/components/utils/vscodePaths.test.ts +41 -0
- package/vitepress/components/utils/vscodePaths.ts +52 -0
- package/vitepress/config/index.ts +2 -0
- package/vitepress/plugins/localSearchIndexBuilder.ts +209 -0
- package/vitepress/plugins/localSearchReindex.test.ts +46 -0
- package/vitepress/plugins/localSearchReindexLogic.ts +50 -0
- package/vitepress/plugins/localSearchReindexPatch.test.ts +30 -0
- package/vitepress/plugins/localSearchReindexPatch.ts +37 -0
- package/vitepress/plugins/localSearchReindexPlugin.ts +318 -0
- package/vitepress/plugins/sidebarStructurePlugin.ts +209 -63
- package/vitepress/theme/index.ts +8 -0
- package/vitepress/theme/styles/base.scss +0 -8
- package/vitepress/theme/styles/components/markmap.scss +23 -2
- package/vitepress/theme/styles/components/swiper.scss +1 -1
- package/vitepress/theme/styles/doc-layout.scss +212 -0
- package/vitepress/theme/styles/index.scss +3 -0
- package/vitepress/theme/styles/layout.scss +60 -10
- package/vitepress/assets/icons/icon__mindmap.svg +0 -1
- package/vitepress/assets/icons/icon__setting.svg +0 -1
- package/vitepress/components/SidebarCard/MindMapView.vue +0 -488
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
applySidebarCollapsedState,
|
|
5
|
+
buildPostDropCollapsePatch,
|
|
6
|
+
collapseKeyForLine,
|
|
7
|
+
collapseKeyForNote,
|
|
8
|
+
collectSidebarCollapsedState,
|
|
9
|
+
mergeCollapseStore,
|
|
10
|
+
normalizeCollapseStore,
|
|
11
|
+
shouldSuppressActiveItemScroll,
|
|
12
|
+
suppressActiveItemScroll,
|
|
13
|
+
} from './sidebarCollapsedState'
|
|
14
|
+
|
|
15
|
+
describe('sidebarCollapsedState', () => {
|
|
16
|
+
const tree = [
|
|
17
|
+
{
|
|
18
|
+
link: '/notes/0037. Writing/README',
|
|
19
|
+
collapsed: false,
|
|
20
|
+
tocLineIndex: 10,
|
|
21
|
+
items: [
|
|
22
|
+
{
|
|
23
|
+
link: '/notes/0038. Mode/README',
|
|
24
|
+
collapsed: true,
|
|
25
|
+
items: [],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
link: '/notes/0019. Author/README',
|
|
31
|
+
collapsed: true,
|
|
32
|
+
tocLineIndex: 20,
|
|
33
|
+
items: [
|
|
34
|
+
{
|
|
35
|
+
link: '/notes/0036. Workflow/README',
|
|
36
|
+
collapsed: true,
|
|
37
|
+
items: [],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
text: 'Giscus',
|
|
43
|
+
collapsed: false,
|
|
44
|
+
tocLineIndex: 34,
|
|
45
|
+
items: [
|
|
46
|
+
{
|
|
47
|
+
link: '/notes/0003. Giscus/README',
|
|
48
|
+
collapsed: true,
|
|
49
|
+
items: [],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
it('collectSidebarCollapsedState records note and line keys', () => {
|
|
56
|
+
expect(collectSidebarCollapsedState(tree)).toEqual({
|
|
57
|
+
[collapseKeyForNote('0037')]: false,
|
|
58
|
+
[collapseKeyForNote('0019')]: true,
|
|
59
|
+
[collapseKeyForLine(34)]: false,
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('applySidebarCollapsedState restores cached expansion', () => {
|
|
64
|
+
const restored = applySidebarCollapsedState(
|
|
65
|
+
tree.map((item) => ({ ...item, collapsed: true })),
|
|
66
|
+
{ [collapseKeyForNote('0037')]: false },
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
expect(restored[0].collapsed).toBe(false)
|
|
70
|
+
expect(restored[1].collapsed).toBe(true)
|
|
71
|
+
expect(restored[2].collapsed).toBe(true)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('applySidebarCollapsedState restores pure folder by line key', () => {
|
|
75
|
+
const restored = applySidebarCollapsedState(
|
|
76
|
+
[{ ...tree[2], collapsed: true }],
|
|
77
|
+
{ [collapseKeyForLine(34)]: false },
|
|
78
|
+
)
|
|
79
|
+
expect(restored[0].collapsed).toBe(false)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('normalizeCollapseStore migrates v1 note keys', () => {
|
|
83
|
+
expect(normalizeCollapseStore({ '0019': false, '0037': true })).toEqual({
|
|
84
|
+
[collapseKeyForNote('0019')]: false,
|
|
85
|
+
[collapseKeyForNote('0037')]: true,
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('normalizeCollapseStore reads v2 entries', () => {
|
|
90
|
+
expect(
|
|
91
|
+
normalizeCollapseStore({
|
|
92
|
+
v: 2,
|
|
93
|
+
entries: { [collapseKeyForLine(34)]: false },
|
|
94
|
+
}),
|
|
95
|
+
).toEqual({
|
|
96
|
+
[collapseKeyForLine(34)]: false,
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('mergeCollapseStore applies patch', () => {
|
|
101
|
+
expect(
|
|
102
|
+
mergeCollapseStore(
|
|
103
|
+
{ [collapseKeyForNote('0019')]: true },
|
|
104
|
+
{ [collapseKeyForNote('0019')]: false },
|
|
105
|
+
),
|
|
106
|
+
).toEqual({ [collapseKeyForNote('0019')]: false })
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('buildPostDropCollapsePatch returns empty for moveAfter', () => {
|
|
110
|
+
expect(
|
|
111
|
+
buildPostDropCollapsePatch(
|
|
112
|
+
{ action: 'moveAfter', target_uuid: 'note:0039' },
|
|
113
|
+
{ sourceCollapseKey: null, wasExpanded: false },
|
|
114
|
+
),
|
|
115
|
+
).toEqual({})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('buildPostDropCollapsePatch expands prependChild target folder', () => {
|
|
119
|
+
expect(
|
|
120
|
+
buildPostDropCollapsePatch(
|
|
121
|
+
{ action: 'prependChild', target_uuid: 'line:41' },
|
|
122
|
+
{ sourceCollapseKey: null, wasExpanded: false },
|
|
123
|
+
),
|
|
124
|
+
).toEqual({ [collapseKeyForLine(41)]: false })
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('buildPostDropCollapsePatch expands prependChild target note', () => {
|
|
128
|
+
expect(
|
|
129
|
+
buildPostDropCollapsePatch(
|
|
130
|
+
{ action: 'prependChild', target_uuid: 'note:0019' },
|
|
131
|
+
{ sourceCollapseKey: null, wasExpanded: false },
|
|
132
|
+
),
|
|
133
|
+
).toEqual({ [collapseKeyForNote('0019')]: false })
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('buildPostDropCollapsePatch expands folder target via targetCollapseKey', () => {
|
|
137
|
+
expect(
|
|
138
|
+
buildPostDropCollapsePatch(
|
|
139
|
+
{
|
|
140
|
+
action: 'prependChild',
|
|
141
|
+
target_uuid: 'folder:TNotes 笔记书写规范',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
sourceCollapseKey: null,
|
|
145
|
+
wasExpanded: false,
|
|
146
|
+
targetCollapseKey: collapseKeyForLine(41),
|
|
147
|
+
},
|
|
148
|
+
),
|
|
149
|
+
).toEqual({ [collapseKeyForLine(41)]: false })
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('buildPostDropCollapsePatch preserves expanded source folder', () => {
|
|
153
|
+
expect(
|
|
154
|
+
buildPostDropCollapsePatch(
|
|
155
|
+
{ action: 'moveAfter', target_uuid: 'note:0039' },
|
|
156
|
+
{
|
|
157
|
+
sourceCollapseKey: collapseKeyForNote('0019'),
|
|
158
|
+
wasExpanded: true,
|
|
159
|
+
},
|
|
160
|
+
),
|
|
161
|
+
).toEqual({ [collapseKeyForNote('0019')]: false })
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('shouldSuppressActiveItemScroll respects TTL', () => {
|
|
165
|
+
const key = 'test-suppress-active-scroll'
|
|
166
|
+
const storage = new Map<string, string>()
|
|
167
|
+
|
|
168
|
+
const sessionStorage = {
|
|
169
|
+
getItem: (name: string) => storage.get(name) ?? null,
|
|
170
|
+
setItem: (name: string, value: string) => {
|
|
171
|
+
storage.set(name, value)
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
vi.stubGlobal('window', { sessionStorage })
|
|
176
|
+
|
|
177
|
+
suppressActiveItemScroll(key, 2000, 1000)
|
|
178
|
+
expect(shouldSuppressActiveItemScroll(key, 1500)).toBe(true)
|
|
179
|
+
expect(shouldSuppressActiveItemScroll(key, 3500)).toBe(false)
|
|
180
|
+
|
|
181
|
+
vi.unstubAllGlobals()
|
|
182
|
+
})
|
|
183
|
+
})
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 侧边栏文件夹展开/折叠状态(localStorage)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { extractNoteIndexFromLink } from './sidebarDragLogic'
|
|
6
|
+
|
|
7
|
+
export interface SidebarCollapseNode {
|
|
8
|
+
link?: string
|
|
9
|
+
collapsed?: boolean
|
|
10
|
+
tocLineIndex?: number
|
|
11
|
+
items?: SidebarCollapseNode[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type CollapseStoreV2 = {
|
|
15
|
+
v: 2
|
|
16
|
+
entries: Record<string, boolean>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function collapseKeyForNote(noteIndex: string): string {
|
|
20
|
+
return `note:${noteIndex}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function collapseKeyForLine(tocLineIndex: number): string {
|
|
24
|
+
return `line:${tocLineIndex}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getCollapseKey(item: SidebarCollapseNode): string | null {
|
|
28
|
+
if (!item.items?.length) return null
|
|
29
|
+
|
|
30
|
+
const noteIndex = extractNoteIndexFromLink(item.link)
|
|
31
|
+
if (noteIndex) return collapseKeyForNote(noteIndex)
|
|
32
|
+
|
|
33
|
+
if (item.tocLineIndex !== undefined) {
|
|
34
|
+
return collapseKeyForLine(item.tocLineIndex)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function collectSidebarCollapsedState(
|
|
41
|
+
items: SidebarCollapseNode[],
|
|
42
|
+
result: Record<string, boolean> = {},
|
|
43
|
+
): Record<string, boolean> {
|
|
44
|
+
for (const item of items) {
|
|
45
|
+
const key = getCollapseKey(item)
|
|
46
|
+
if (key) {
|
|
47
|
+
result[key] = !!item.collapsed
|
|
48
|
+
}
|
|
49
|
+
if (item.items?.length) {
|
|
50
|
+
collectSidebarCollapsedState(item.items, result)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function normalizeCollapseStore(
|
|
57
|
+
raw: unknown,
|
|
58
|
+
): Record<string, boolean> {
|
|
59
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
60
|
+
return {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const obj = raw as Record<string, unknown>
|
|
64
|
+
|
|
65
|
+
if (obj.v === 2 && obj.entries && typeof obj.entries === 'object') {
|
|
66
|
+
const entries = obj.entries as Record<string, unknown>
|
|
67
|
+
const result: Record<string, boolean> = {}
|
|
68
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
69
|
+
if (/^(note:\d{4}|line:\d+)$/.test(key)) {
|
|
70
|
+
result[key] = !!value
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result: Record<string, boolean> = {}
|
|
77
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
78
|
+
if (/^\d{4}$/.test(key)) {
|
|
79
|
+
result[collapseKeyForNote(key)] = !!value
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return result
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function applySidebarCollapsedState<T extends SidebarCollapseNode>(
|
|
86
|
+
items: T[],
|
|
87
|
+
state: Record<string, boolean>,
|
|
88
|
+
): T[] {
|
|
89
|
+
return items.map((item) => {
|
|
90
|
+
const key = getCollapseKey(item)
|
|
91
|
+
const next = { ...item }
|
|
92
|
+
|
|
93
|
+
if (key && key in state) {
|
|
94
|
+
next.collapsed = state[key]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (item.items?.length) {
|
|
98
|
+
next.items = applySidebarCollapsedState(item.items, state)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return next
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function loadSidebarCollapsedState(
|
|
106
|
+
storageKey: string,
|
|
107
|
+
): Record<string, boolean> {
|
|
108
|
+
if (typeof window === 'undefined') return {}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const raw = window.localStorage.getItem(storageKey)
|
|
112
|
+
if (!raw) return {}
|
|
113
|
+
|
|
114
|
+
return normalizeCollapseStore(JSON.parse(raw) as unknown)
|
|
115
|
+
} catch {
|
|
116
|
+
return {}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function saveSidebarCollapsedState(
|
|
121
|
+
storageKey: string,
|
|
122
|
+
items: SidebarCollapseNode[],
|
|
123
|
+
): void {
|
|
124
|
+
if (typeof window === 'undefined') return
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const payload: CollapseStoreV2 = {
|
|
128
|
+
v: 2,
|
|
129
|
+
entries: collectSidebarCollapsedState(items),
|
|
130
|
+
}
|
|
131
|
+
window.localStorage.setItem(storageKey, JSON.stringify(payload))
|
|
132
|
+
} catch {
|
|
133
|
+
// ignore quota / privacy mode
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function mergeCollapseStore(
|
|
138
|
+
base: Record<string, boolean>,
|
|
139
|
+
patch: Record<string, boolean>,
|
|
140
|
+
): Record<string, boolean> {
|
|
141
|
+
return { ...base, ...patch }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function saveCollapseEntries(
|
|
145
|
+
storageKey: string,
|
|
146
|
+
entries: Record<string, boolean>,
|
|
147
|
+
): void {
|
|
148
|
+
if (typeof window === 'undefined') return
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const payload: CollapseStoreV2 = { v: 2, entries }
|
|
152
|
+
window.localStorage.setItem(storageKey, JSON.stringify(payload))
|
|
153
|
+
} catch {
|
|
154
|
+
// ignore quota / privacy mode
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function getCollapseKeyForTreeItem(
|
|
159
|
+
item: SidebarCollapseNode,
|
|
160
|
+
): string | null {
|
|
161
|
+
return getCollapseKey(item)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface PostDropCollapseInput {
|
|
165
|
+
action: 'moveAfter' | 'prependChild'
|
|
166
|
+
target_uuid?: string
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface PostDropCollapseMeta {
|
|
170
|
+
sourceCollapseKey: string | null
|
|
171
|
+
wasExpanded: boolean
|
|
172
|
+
/** prependChild 目标为 folder:path 等时由调用方解析 */
|
|
173
|
+
targetCollapseKey?: string | null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function buildPostDropCollapsePatch(
|
|
177
|
+
payload: PostDropCollapseInput,
|
|
178
|
+
meta: PostDropCollapseMeta,
|
|
179
|
+
): Record<string, boolean> {
|
|
180
|
+
const patch: Record<string, boolean> = {}
|
|
181
|
+
|
|
182
|
+
if (payload.action === 'prependChild' && payload.target_uuid) {
|
|
183
|
+
if (payload.target_uuid.startsWith('note:')) {
|
|
184
|
+
patch[collapseKeyForNote(payload.target_uuid.slice(5))] = false
|
|
185
|
+
} else if (payload.target_uuid.startsWith('line:')) {
|
|
186
|
+
patch[collapseKeyForLine(Number(payload.target_uuid.slice(5)))] = false
|
|
187
|
+
} else if (
|
|
188
|
+
payload.target_uuid.startsWith('folder:') &&
|
|
189
|
+
meta.targetCollapseKey
|
|
190
|
+
) {
|
|
191
|
+
patch[meta.targetCollapseKey] = false
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (
|
|
196
|
+
payload.action === 'prependChild' &&
|
|
197
|
+
meta.targetCollapseKey &&
|
|
198
|
+
!(meta.targetCollapseKey in patch)
|
|
199
|
+
) {
|
|
200
|
+
patch[meta.targetCollapseKey] = false
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (meta.wasExpanded && meta.sourceCollapseKey) {
|
|
204
|
+
patch[meta.sourceCollapseKey] = false
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return patch
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const DEFAULT_SUPPRESS_ACTIVE_SCROLL_MS = 2000
|
|
211
|
+
|
|
212
|
+
export function shouldSuppressActiveItemScroll(
|
|
213
|
+
storageKey: string,
|
|
214
|
+
now = Date.now(),
|
|
215
|
+
): boolean {
|
|
216
|
+
if (typeof window === 'undefined') return false
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const until = Number(window.sessionStorage.getItem(storageKey) || 0)
|
|
220
|
+
return now < until
|
|
221
|
+
} catch {
|
|
222
|
+
return false
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function suppressActiveItemScroll(
|
|
227
|
+
storageKey: string,
|
|
228
|
+
durationMs = DEFAULT_SUPPRESS_ACTIVE_SCROLL_MS,
|
|
229
|
+
now = Date.now(),
|
|
230
|
+
): void {
|
|
231
|
+
if (typeof window === 'undefined') return
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
window.sessionStorage.setItem(
|
|
235
|
+
storageKey,
|
|
236
|
+
String(now + durationMs),
|
|
237
|
+
)
|
|
238
|
+
} catch {
|
|
239
|
+
// ignore quota / privacy mode
|
|
240
|
+
}
|
|
241
|
+
}
|