@longzai-intelligence/pagination 0.0.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.
Files changed (62) hide show
  1. package/.turbo/turbo-build.log +22 -0
  2. package/.turbo/turbo-lint.log +1 -0
  3. package/.turbo/turbo-test$colon$coverage.log +36 -0
  4. package/.turbo/turbo-test.log +14 -0
  5. package/.turbo/turbo-typecheck.log +4 -0
  6. package/CHANGELOG.md +11 -0
  7. package/README.md +218 -0
  8. package/coverage/base.css +224 -0
  9. package/coverage/block-navigation.js +87 -0
  10. package/coverage/coverage-final.json +12 -0
  11. package/coverage/favicon.png +0 -0
  12. package/coverage/index.html +161 -0
  13. package/coverage/prettify.css +1 -0
  14. package/coverage/prettify.js +2 -0
  15. package/coverage/sort-arrow-sprite.png +0 -0
  16. package/coverage/sorter.js +210 -0
  17. package/coverage/src/adapters/index.html +131 -0
  18. package/coverage/src/adapters/index.ts.html +118 -0
  19. package/coverage/src/adapters/typeorm.adapter.ts.html +940 -0
  20. package/coverage/src/index.html +116 -0
  21. package/coverage/src/index.ts.html +253 -0
  22. package/coverage/src/types/index.html +176 -0
  23. package/coverage/src/types/index.ts.html +133 -0
  24. package/coverage/src/types/pagination.types.ts.html +292 -0
  25. package/coverage/src/types/result.types.ts.html +415 -0
  26. package/coverage/src/types/sort.types.ts.html +265 -0
  27. package/coverage/src/types/typeorm.d.ts.html +196 -0
  28. package/coverage/src/utils/index.html +146 -0
  29. package/coverage/src/utils/index.ts.html +178 -0
  30. package/coverage/src/utils/pagination.util.ts.html +703 -0
  31. package/coverage/src/utils/validation.util.ts.html +535 -0
  32. package/dist/index.cjs +1 -0
  33. package/dist/index.d.cts +117 -0
  34. package/dist/index.d.mts +117 -0
  35. package/dist/index.mjs +1 -0
  36. package/eslint.config.ts +3 -0
  37. package/package.json +62 -0
  38. package/src/__tests__/index.test.ts +66 -0
  39. package/src/__tests__/pagination.util.test.ts +253 -0
  40. package/src/__tests__/typeorm.adapter.test.ts +214 -0
  41. package/src/__tests__/validation.util.test.ts +122 -0
  42. package/src/adapters/index.ts +11 -0
  43. package/src/adapters/typeorm.adapter.ts +285 -0
  44. package/src/index.ts +56 -0
  45. package/src/types/index.ts +16 -0
  46. package/src/types/pagination.types.ts +69 -0
  47. package/src/types/result.types.ts +110 -0
  48. package/src/types/sort.types.ts +60 -0
  49. package/src/types/typeorm.d.ts +37 -0
  50. package/src/utils/index.ts +31 -0
  51. package/src/utils/pagination.util.ts +206 -0
  52. package/src/utils/validation.util.ts +150 -0
  53. package/tsconfig/.cache/app.tsbuildinfo +1 -0
  54. package/tsconfig/.cache/build.tsbuildinfo +1 -0
  55. package/tsconfig/.cache/node.tsbuildinfo +1 -0
  56. package/tsconfig/.cache/test.tsbuildinfo +1 -0
  57. package/tsconfig/app.json +12 -0
  58. package/tsconfig/node.json +11 -0
  59. package/tsconfig/test.json +14 -0
  60. package/tsconfig.json +9 -0
  61. package/tsdown.config.ts +6 -0
  62. package/vitest.config.ts +3 -0
@@ -0,0 +1,22 @@
1
+ $ tsdown
2
+ ℹ tsdown v0.21.7 powered by rolldown v1.0.0-rc.12
3
+ ℹ config file: /Volumes/JZAO/j-projects/workspaces/longzai/longzai-intelligence/longzai-intelligence/packages/pagination/tsdown.config.ts
4
+ (node:62059) ExperimentalWarning: Type Stripping is an experimental feature and might change at any time
5
+ (Use `node --trace-warnings ...` to show where the warning was created)
6
+ ℹ entry: src/index.ts
7
+ ℹ tsconfig: tsconfig/app.json
8
+ ℹ Build start
9
+ ℹ Cleaning 8 files
10
+ ℹ [CJS] dist/index.cjs 9.63 kB │ gzip: 2.45 kB
11
+ ℹ [CJS] dist/index.cjs.map 19.04 kB │ gzip: 4.55 kB
12
+ ℹ [CJS] 2 files, total: 28.66 kB
13
+ ℹ [CJS] dist/index.d.cts.map 2.74 kB │ gzip: 0.82 kB
14
+ ℹ [CJS] dist/index.d.cts 5.36 kB │ gzip: 1.21 kB
15
+ ℹ [CJS] 2 files, total: 8.10 kB
16
+ ✔ Build complete in 679ms
17
+ ℹ [ESM] dist/index.mjs 9.02 kB │ gzip: 2.33 kB
18
+ ℹ [ESM] dist/index.mjs.map 19.04 kB │ gzip: 4.55 kB
19
+ ℹ [ESM] dist/index.d.mts.map 2.74 kB │ gzip: 0.82 kB
20
+ ℹ [ESM] dist/index.d.mts 5.36 kB │ gzip: 1.21 kB
21
+ ℹ [ESM] 4 files, total: 36.16 kB
22
+ ✔ Build complete in 680ms
@@ -0,0 +1 @@
1
+ $ eslint .
@@ -0,0 +1,36 @@
1
+ $ vitest run --coverage
2
+
3
+  RUN  v4.1.3 /Volumes/JZAO/j-projects/workspaces/longzai/longzai-intelligence/longzai-intelligence/packages/pagination
4
+ Coverage enabled with v8
5
+
6
+ ✓ src/__tests__/pagination.util.test.ts (41 tests) 3ms
7
+ ✓ src/__tests__/index.test.ts (6 tests) 14ms
8
+ ✓ src/__tests__/validation.util.test.ts (12 tests) 3ms
9
+ ✓ src/__tests__/typeorm.adapter.test.ts (9 tests) 4ms
10
+
11
+  Test Files  4 passed (4)
12
+  Tests  68 passed (68)
13
+  Start at  01:27:15
14
+  Duration  162ms (transform 99ms, setup 0ms, import 126ms, tests 24ms, environment 0ms)
15
+
16
+  % Coverage report from v8
17
+ -------------------|---------|----------|---------|---------|-------------------
18
+ File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
19
+ -------------------|---------|----------|---------|---------|-------------------
20
+ All files | 100 | 100 | 100 | 100 |
21
+ src | 0 | 0 | 0 | 0 |
22
+ index.ts | 0 | 0 | 0 | 0 |
23
+ src/adapters | 100 | 100 | 100 | 100 |
24
+ index.ts | 0 | 0 | 0 | 0 |
25
+ ...rm.adapter.ts | 100 | 100 | 100 | 100 |
26
+ src/types | 100 | 100 | 100 | 100 |
27
+ index.ts | 0 | 0 | 0 | 0 |
28
+ ...tion.types.ts | 100 | 100 | 100 | 100 |
29
+ result.types.ts | 0 | 0 | 0 | 0 |
30
+ sort.types.ts | 0 | 0 | 0 | 0 |
31
+ typeorm.d.ts | 0 | 0 | 0 | 0 |
32
+ src/utils | 100 | 100 | 100 | 100 |
33
+ index.ts | 0 | 0 | 0 | 0 |
34
+ ...ation.util.ts | 100 | 100 | 100 | 100 |
35
+ ...ation.util.ts | 100 | 100 | 100 | 100 |
36
+ -------------------|---------|----------|---------|---------|-------------------
@@ -0,0 +1,14 @@
1
+ $ vitest run
2
+
3
+  RUN  v4.1.3 /Volumes/JZAO/j-projects/workspaces/longzai/longzai-intelligence/longzai-intelligence/packages/pagination
4
+
5
+ ✓ src/__tests__/validation.util.test.ts (12 tests) 4ms
6
+ ✓ src/__tests__/pagination.util.test.ts (41 tests) 6ms
7
+ ✓ src/__tests__/typeorm.adapter.test.ts (9 tests) 7ms
8
+ ✓ src/__tests__/index.test.ts (6 tests) 22ms
9
+
10
+  Test Files  4 passed (4)
11
+  Tests  68 passed (68)
12
+  Start at  01:27:13
13
+  Duration  162ms (transform 133ms, setup 0ms, import 153ms, tests 38ms, environment 0ms)
14
+
@@ -0,0 +1,4 @@
1
+ $ bun run typecheck:app && bun run typecheck:node && bun run typecheck:test
2
+ $ tsc --noEmit -p tsconfig/app.json
3
+ $ tsc --noEmit -p tsconfig/node.json
4
+ $ tsc --noEmit -p tsconfig/test.json
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## [0.0.1] - 2026-04-10
4
+
5
+ ### 功能
6
+
7
+ - 分页类型定义:PaginationParams、OffsetPaginationParams、CursorPaginationParams
8
+ - 排序类型定义:SortOrder、SortParams、MultiSortParams
9
+ - 分页工具函数:normalizePagination、calculateOffset、hasNextPage 等
10
+ - 校验工具函数:isValidPage、isValidPageSize、isValidOffset 等
11
+ - TypeORM 适配器:paginateWithRepository、paginateWithQueryBuilder、createPaginationQueryBuilder
package/README.md ADDED
@@ -0,0 +1,218 @@
1
+ # @longzai-intelligence/pagination
2
+
3
+ 统一的分页功能包,提供类型安全的分页参数、结果类型和工具函数。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ bun add @longzai-intelligence/pagination
9
+ ```
10
+
11
+ ## 特性
12
+
13
+ - 类型安全的分页参数定义
14
+ - 支持 page/pageSize 和 limit/offset 两种分页风格
15
+ - 支持游标分页
16
+ - 提供参数验证工具
17
+ - 提供 TypeORM 适配器(可选)
18
+
19
+ ## 快速开始
20
+
21
+ ### 基本使用
22
+
23
+ ```typescript
24
+ import {
25
+ normalizePagination,
26
+ calculateOffset,
27
+ createPaginatedResult,
28
+ type PaginationParams,
29
+ type PaginatedResult,
30
+ } from '@longzai-intelligence/pagination';
31
+
32
+ // 规范化分页参数
33
+ const params: PaginationParams = { page: 2, pageSize: 20 };
34
+ const { page, pageSize } = normalizePagination(params);
35
+
36
+ // 计算偏移量
37
+ const offset = calculateOffset(page, pageSize);
38
+
39
+ // 创建分页结果
40
+ const result: PaginatedResult<User> = createPaginatedResult(
41
+ users,
42
+ total,
43
+ page,
44
+ pageSize,
45
+ );
46
+ ```
47
+
48
+ ### 参数验证
49
+
50
+ ```typescript
51
+ import {
52
+ isValidPage,
53
+ isValidPageSize,
54
+ isValidPagination,
55
+ } from '@longzai-intelligence/pagination';
56
+
57
+ if (isValidPagination(params)) {
58
+ // 参数有效
59
+ }
60
+
61
+ if (isValidPage(1) && isValidPageSize(20)) {
62
+ // 页码和页面大小有效
63
+ }
64
+ ```
65
+
66
+ ### TypeORM 适配器
67
+
68
+ ```typescript
69
+ import {
70
+ paginateWithRepository,
71
+ paginateWithQueryBuilder,
72
+ } from '@longzai-intelligence/pagination';
73
+
74
+ // 使用 Repository 分页
75
+ const result = await paginateWithRepository(userRepository, {
76
+ page: 1,
77
+ pageSize: 20,
78
+ }, {
79
+ where: { active: true },
80
+ order: { createdAt: 'DESC' },
81
+ });
82
+
83
+ // 使用 QueryBuilder 分页
84
+ const qb = userRepository.createQueryBuilder('user');
85
+ const result = await paginateWithQueryBuilder(qb, { page: 1, pageSize: 20 });
86
+ ```
87
+
88
+ ## API 文档
89
+
90
+ ### 类型
91
+
92
+ #### PaginationParams
93
+
94
+ ```typescript
95
+ interface PaginationParams {
96
+ page?: number; // 页码,从 1 开始
97
+ pageSize?: number; // 每页数量
98
+ }
99
+ ```
100
+
101
+ #### OffsetPaginationParams
102
+
103
+ ```typescript
104
+ interface OffsetPaginationParams {
105
+ limit?: number; // 每页数量
106
+ offset?: number; // 偏移量
107
+ }
108
+ ```
109
+
110
+ #### PaginatedResult
111
+
112
+ ```typescript
113
+ interface PaginatedResult<T> {
114
+ items: T[]; // 数据项
115
+ total: number; // 总记录数
116
+ page: number; // 当前页码
117
+ pageSize: number; // 每页数量
118
+ totalPages: number; // 总页数
119
+ hasNextPage: boolean; // 是否有下一页
120
+ hasPreviousPage: boolean; // 是否有上一页
121
+ }
122
+ ```
123
+
124
+ #### SortParams
125
+
126
+ ```typescript
127
+ interface SortParams<T extends string = string> {
128
+ sortBy?: T; // 排序字段
129
+ sortOrder?: 'asc' | 'desc'; // 排序方向
130
+ }
131
+ ```
132
+
133
+ ### 常量
134
+
135
+ #### PAGINATION_DEFAULTS
136
+
137
+ ```typescript
138
+ const PAGINATION_DEFAULTS = {
139
+ page: 1,
140
+ pageSize: 20,
141
+ maxPageSize: 100,
142
+ minPageSize: 1,
143
+ } as const;
144
+ ```
145
+
146
+ ### 工具函数
147
+
148
+ #### normalizePagination(params?)
149
+
150
+ 规范化分页参数,确保参数在有效范围内。
151
+
152
+ ```typescript
153
+ const { page, pageSize } = normalizePagination({ page: 2, pageSize: 50 });
154
+ ```
155
+
156
+ #### calculateOffset(page, pageSize)
157
+
158
+ 计算偏移量。
159
+
160
+ ```typescript
161
+ const offset = calculateOffset(2, 20); // 20
162
+ ```
163
+
164
+ #### calculateTotalPages(total, pageSize)
165
+
166
+ 计算总页数。
167
+
168
+ ```typescript
169
+ const totalPages = calculateTotalPages(100, 20); // 5
170
+ ```
171
+
172
+ #### hasNextPage(page, totalPages) / hasPreviousPage(page)
173
+
174
+ 判断是否有下一页/上一页。
175
+
176
+ ```typescript
177
+ hasNextPage(1, 5); // true
178
+ hasPreviousPage(1); // false
179
+ ```
180
+
181
+ #### offsetToPage(offset, limit) / pageToOffset(page, pageSize)
182
+
183
+ 分页风格转换。
184
+
185
+ ```typescript
186
+ offsetToPage(20, 20); // { page: 2, pageSize: 20 }
187
+ pageToOffset(2, 20); // { offset: 20, limit: 20 }
188
+ ```
189
+
190
+ ### 验证函数
191
+
192
+ #### isValidPage(page)
193
+
194
+ 验证页码是否有效(正整数)。
195
+
196
+ #### isValidPageSize(pageSize)
197
+
198
+ 验证每页数量是否有效(1-100 之间的整数)。
199
+
200
+ #### isValidOffset(offset)
201
+
202
+ 验证偏移量是否有效(非负整数)。
203
+
204
+ #### isValidLimit(limit)
205
+
206
+ 验证限制数量是否有效(1-100 之间的整数)。
207
+
208
+ #### isValidPagination(params)
209
+
210
+ 验证分页参数对象是否有效。
211
+
212
+ #### isValidOffsetPagination(params)
213
+
214
+ 验证 offset 分页参数对象是否有效。
215
+
216
+ ## 许可证
217
+
218
+ MIT
@@ -0,0 +1,224 @@
1
+ body, html {
2
+ margin:0; padding: 0;
3
+ height: 100%;
4
+ }
5
+ body {
6
+ font-family: Helvetica Neue, Helvetica, Arial;
7
+ font-size: 14px;
8
+ color:#333;
9
+ }
10
+ .small { font-size: 12px; }
11
+ *, *:after, *:before {
12
+ -webkit-box-sizing:border-box;
13
+ -moz-box-sizing:border-box;
14
+ box-sizing:border-box;
15
+ }
16
+ h1 { font-size: 20px; margin: 0;}
17
+ h2 { font-size: 14px; }
18
+ pre {
19
+ font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20
+ margin: 0;
21
+ padding: 0;
22
+ -moz-tab-size: 2;
23
+ -o-tab-size: 2;
24
+ tab-size: 2;
25
+ }
26
+ a { color:#0074D9; text-decoration:none; }
27
+ a:hover { text-decoration:underline; }
28
+ .strong { font-weight: bold; }
29
+ .space-top1 { padding: 10px 0 0 0; }
30
+ .pad2y { padding: 20px 0; }
31
+ .pad1y { padding: 10px 0; }
32
+ .pad2x { padding: 0 20px; }
33
+ .pad2 { padding: 20px; }
34
+ .pad1 { padding: 10px; }
35
+ .space-left2 { padding-left:55px; }
36
+ .space-right2 { padding-right:20px; }
37
+ .center { text-align:center; }
38
+ .clearfix { display:block; }
39
+ .clearfix:after {
40
+ content:'';
41
+ display:block;
42
+ height:0;
43
+ clear:both;
44
+ visibility:hidden;
45
+ }
46
+ .fl { float: left; }
47
+ @media only screen and (max-width:640px) {
48
+ .col3 { width:100%; max-width:100%; }
49
+ .hide-mobile { display:none!important; }
50
+ }
51
+
52
+ .quiet {
53
+ color: #7f7f7f;
54
+ color: rgba(0,0,0,0.5);
55
+ }
56
+ .quiet a { opacity: 0.7; }
57
+
58
+ .fraction {
59
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60
+ font-size: 10px;
61
+ color: #555;
62
+ background: #E8E8E8;
63
+ padding: 4px 5px;
64
+ border-radius: 3px;
65
+ vertical-align: middle;
66
+ }
67
+
68
+ div.path a:link, div.path a:visited { color: #333; }
69
+ table.coverage {
70
+ border-collapse: collapse;
71
+ margin: 10px 0 0 0;
72
+ padding: 0;
73
+ }
74
+
75
+ table.coverage td {
76
+ margin: 0;
77
+ padding: 0;
78
+ vertical-align: top;
79
+ }
80
+ table.coverage td.line-count {
81
+ text-align: right;
82
+ padding: 0 5px 0 20px;
83
+ }
84
+ table.coverage td.line-coverage {
85
+ text-align: right;
86
+ padding-right: 10px;
87
+ min-width:20px;
88
+ }
89
+
90
+ table.coverage td span.cline-any {
91
+ display: inline-block;
92
+ padding: 0 5px;
93
+ width: 100%;
94
+ }
95
+ .missing-if-branch {
96
+ display: inline-block;
97
+ margin-right: 5px;
98
+ border-radius: 3px;
99
+ position: relative;
100
+ padding: 0 4px;
101
+ background: #333;
102
+ color: yellow;
103
+ }
104
+
105
+ .skip-if-branch {
106
+ display: none;
107
+ margin-right: 10px;
108
+ position: relative;
109
+ padding: 0 4px;
110
+ background: #ccc;
111
+ color: white;
112
+ }
113
+ .missing-if-branch .typ, .skip-if-branch .typ {
114
+ color: inherit !important;
115
+ }
116
+ .coverage-summary {
117
+ border-collapse: collapse;
118
+ width: 100%;
119
+ }
120
+ .coverage-summary tr { border-bottom: 1px solid #bbb; }
121
+ .keyline-all { border: 1px solid #ddd; }
122
+ .coverage-summary td, .coverage-summary th { padding: 10px; }
123
+ .coverage-summary tbody { border: 1px solid #bbb; }
124
+ .coverage-summary td { border-right: 1px solid #bbb; }
125
+ .coverage-summary td:last-child { border-right: none; }
126
+ .coverage-summary th {
127
+ text-align: left;
128
+ font-weight: normal;
129
+ white-space: nowrap;
130
+ }
131
+ .coverage-summary th.file { border-right: none !important; }
132
+ .coverage-summary th.pct { }
133
+ .coverage-summary th.pic,
134
+ .coverage-summary th.abs,
135
+ .coverage-summary td.pct,
136
+ .coverage-summary td.abs { text-align: right; }
137
+ .coverage-summary td.file { white-space: nowrap; }
138
+ .coverage-summary td.pic { min-width: 120px !important; }
139
+ .coverage-summary tfoot td { }
140
+
141
+ .coverage-summary .sorter {
142
+ height: 10px;
143
+ width: 7px;
144
+ display: inline-block;
145
+ margin-left: 0.5em;
146
+ background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147
+ }
148
+ .coverage-summary .sorted .sorter {
149
+ background-position: 0 -20px;
150
+ }
151
+ .coverage-summary .sorted-desc .sorter {
152
+ background-position: 0 -10px;
153
+ }
154
+ .status-line { height: 10px; }
155
+ /* yellow */
156
+ .cbranch-no { background: yellow !important; color: #111; }
157
+ /* dark red */
158
+ .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159
+ .low .chart { border:1px solid #C21F39 }
160
+ .highlighted,
161
+ .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162
+ background: #C21F39 !important;
163
+ }
164
+ /* medium red */
165
+ .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166
+ /* light red */
167
+ .low, .cline-no { background:#FCE1E5 }
168
+ /* light green */
169
+ .high, .cline-yes { background:rgb(230,245,208) }
170
+ /* medium green */
171
+ .cstat-yes { background:rgb(161,215,106) }
172
+ /* dark green */
173
+ .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174
+ .high .chart { border:1px solid rgb(77,146,33) }
175
+ /* dark yellow (gold) */
176
+ .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
177
+ .medium .chart { border:1px solid #f9cd0b; }
178
+ /* light yellow */
179
+ .medium { background: #fff4c2; }
180
+
181
+ .cstat-skip { background: #ddd; color: #111; }
182
+ .fstat-skip { background: #ddd; color: #111 !important; }
183
+ .cbranch-skip { background: #ddd !important; color: #111; }
184
+
185
+ span.cline-neutral { background: #eaeaea; }
186
+
187
+ .coverage-summary td.empty {
188
+ opacity: .5;
189
+ padding-top: 4px;
190
+ padding-bottom: 4px;
191
+ line-height: 1;
192
+ color: #888;
193
+ }
194
+
195
+ .cover-fill, .cover-empty {
196
+ display:inline-block;
197
+ height: 12px;
198
+ }
199
+ .chart {
200
+ line-height: 0;
201
+ }
202
+ .cover-empty {
203
+ background: white;
204
+ }
205
+ .cover-full {
206
+ border-right: none !important;
207
+ }
208
+ pre.prettyprint {
209
+ border: none !important;
210
+ padding: 0 !important;
211
+ margin: 0 !important;
212
+ }
213
+ .com { color: #999 !important; }
214
+ .ignore-none { color: #999; font-weight: normal; }
215
+
216
+ .wrapper {
217
+ min-height: 100%;
218
+ height: auto !important;
219
+ height: 100%;
220
+ margin: 0 auto -48px;
221
+ }
222
+ .footer, .push {
223
+ height: 48px;
224
+ }
@@ -0,0 +1,87 @@
1
+ /* eslint-disable */
2
+ var jumpToCode = (function init() {
3
+ // Classes of code we would like to highlight in the file view
4
+ var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
5
+
6
+ // Elements to highlight in the file listing view
7
+ var fileListingElements = ['td.pct.low'];
8
+
9
+ // We don't want to select elements that are direct descendants of another match
10
+ var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
11
+
12
+ // Selector that finds elements on the page to which we can jump
13
+ var selector =
14
+ fileListingElements.join(', ') +
15
+ ', ' +
16
+ notSelector +
17
+ missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
18
+
19
+ // The NodeList of matching elements
20
+ var missingCoverageElements = document.querySelectorAll(selector);
21
+
22
+ var currentIndex;
23
+
24
+ function toggleClass(index) {
25
+ missingCoverageElements
26
+ .item(currentIndex)
27
+ .classList.remove('highlighted');
28
+ missingCoverageElements.item(index).classList.add('highlighted');
29
+ }
30
+
31
+ function makeCurrent(index) {
32
+ toggleClass(index);
33
+ currentIndex = index;
34
+ missingCoverageElements.item(index).scrollIntoView({
35
+ behavior: 'smooth',
36
+ block: 'center',
37
+ inline: 'center'
38
+ });
39
+ }
40
+
41
+ function goToPrevious() {
42
+ var nextIndex = 0;
43
+ if (typeof currentIndex !== 'number' || currentIndex === 0) {
44
+ nextIndex = missingCoverageElements.length - 1;
45
+ } else if (missingCoverageElements.length > 1) {
46
+ nextIndex = currentIndex - 1;
47
+ }
48
+
49
+ makeCurrent(nextIndex);
50
+ }
51
+
52
+ function goToNext() {
53
+ var nextIndex = 0;
54
+
55
+ if (
56
+ typeof currentIndex === 'number' &&
57
+ currentIndex < missingCoverageElements.length - 1
58
+ ) {
59
+ nextIndex = currentIndex + 1;
60
+ }
61
+
62
+ makeCurrent(nextIndex);
63
+ }
64
+
65
+ return function jump(event) {
66
+ if (
67
+ document.getElementById('fileSearch') === document.activeElement &&
68
+ document.activeElement != null
69
+ ) {
70
+ // if we're currently focused on the search input, we don't want to navigate
71
+ return;
72
+ }
73
+
74
+ switch (event.which) {
75
+ case 78: // n
76
+ case 74: // j
77
+ goToNext();
78
+ break;
79
+ case 66: // b
80
+ case 75: // k
81
+ case 80: // p
82
+ goToPrevious();
83
+ break;
84
+ }
85
+ };
86
+ })();
87
+ window.addEventListener('keydown', jumpToCode);