@fiscozen/checkbox 1.0.1 → 1.1.2
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/CHANGELOG.md +14 -0
- package/README.md +142 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +1031 -0
- package/coverage/coverage-final.json +7 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +131 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/coverage/src/FzCheckbox.vue.html +1489 -0
- package/coverage/src/FzCheckboxGroup.vue.html +682 -0
- package/coverage/src/common.ts.html +157 -0
- package/coverage/src/components/ErrorAlert.vue.html +268 -0
- package/coverage/src/components/FzCheckboxGroupOption.vue.html +652 -0
- package/coverage/src/components/index.html +131 -0
- package/coverage/src/index.html +161 -0
- package/coverage/src/utils.ts.html +265 -0
- package/dist/checkbox.js +736 -555
- package/dist/checkbox.umd.cjs +2 -2
- package/dist/src/FzCheckboxCard.vue.d.ts +240 -0
- package/dist/src/FzCheckboxGroup.vue.d.ts +8 -2
- package/dist/src/__tests__/FzCheckboxCard.spec.d.ts +1 -0
- package/dist/src/common.d.ts +16 -5
- package/dist/src/index.d.ts +2 -1
- package/dist/src/types.d.ts +71 -2
- package/dist/style.css +1 -1
- package/package.json +4 -4
- package/src/FzCheckboxCard.vue +209 -0
- package/src/FzCheckboxGroup.vue +33 -10
- package/src/__tests__/FzCheckboxCard.spec.ts +423 -0
- package/src/__tests__/__snapshots__/FzCheckbox.spec.ts.snap +5 -11
- package/src/__tests__/__snapshots__/FzCheckboxCard.spec.ts.snap +17 -0
- package/src/__tests__/__snapshots__/FzCheckboxGroup.spec.ts.snap +5 -11
- package/src/common.ts +19 -0
- package/src/components/ErrorAlert.vue +4 -4
- package/src/index.ts +6 -1
- package/src/types.ts +86 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
ADDED
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A fully accessible, WCAG 2.1 AA compliant checkbox component library for Vue 3 a
|
|
|
7
7
|
- **WCAG 2.1 AA Compliant** - Full accessibility support with comprehensive ARIA attributes
|
|
8
8
|
- **Flexible Data Models** - Support for boolean (single) and array (multi-select) v-model binding
|
|
9
9
|
- **Hierarchical Structures** - Parent-child checkbox relationships with automatic indeterminate state
|
|
10
|
+
- **Card-style Checkboxes** - Rich selection cards with images, titles, subtitles, and tooltips
|
|
10
11
|
- **Keyboard Navigation** - Complete keyboard accessibility with visible focus indicators
|
|
11
12
|
- **Screen Reader Optimized** - Proper semantic markup and live region announcements
|
|
12
13
|
- **Error Handling** - Built-in validation states with accessible error messages
|
|
@@ -113,6 +114,37 @@ const options = [
|
|
|
113
114
|
</template>
|
|
114
115
|
```
|
|
115
116
|
|
|
117
|
+
### Checkbox Card
|
|
118
|
+
|
|
119
|
+
```vue
|
|
120
|
+
<script setup lang="ts">
|
|
121
|
+
import { ref } from 'vue';
|
|
122
|
+
import { FzCheckboxCard } from '@fiscozen/checkbox';
|
|
123
|
+
|
|
124
|
+
const selected = ref<(string | number)[]>([]);
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<template>
|
|
128
|
+
<div style="display: flex; gap: 12px;">
|
|
129
|
+
<FzCheckboxCard
|
|
130
|
+
v-model="selected"
|
|
131
|
+
label="option-a"
|
|
132
|
+
value="option-a"
|
|
133
|
+
title="Option A"
|
|
134
|
+
subtitle="Description for option A"
|
|
135
|
+
/>
|
|
136
|
+
|
|
137
|
+
<FzCheckboxCard
|
|
138
|
+
v-model="selected"
|
|
139
|
+
label="option-b"
|
|
140
|
+
value="option-b"
|
|
141
|
+
title="Option B"
|
|
142
|
+
subtitle="Description for option B"
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
</template>
|
|
146
|
+
```
|
|
147
|
+
|
|
116
148
|
## API Reference
|
|
117
149
|
|
|
118
150
|
### FzCheckbox
|
|
@@ -191,6 +223,45 @@ v-model: string[]
|
|
|
191
223
|
|
|
192
224
|
---
|
|
193
225
|
|
|
226
|
+
### FzCheckboxCard
|
|
227
|
+
|
|
228
|
+
A card-style checkbox component with support for title, subtitle, optional image, and tooltip. Available in horizontal and vertical layout variants. Uses array v-model for multi-select patterns.
|
|
229
|
+
|
|
230
|
+
#### Props
|
|
231
|
+
|
|
232
|
+
| Prop | Type | Default | Description |
|
|
233
|
+
|------|------|---------|-------------|
|
|
234
|
+
| `label` | `string` | **required** | Accessible label for the underlying checkbox input |
|
|
235
|
+
| `value` | `string \| number` | `label` | Value tracked in the array v-model |
|
|
236
|
+
| `title` | `string` | **required** | Primary title text displayed in the card |
|
|
237
|
+
| `subtitle` | `string` | `undefined` | Optional secondary text below the title |
|
|
238
|
+
| `variant` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction of the card |
|
|
239
|
+
| `imageUrl` | `string` | `undefined` | URL of the card image (required when `variant` is `'vertical'`) |
|
|
240
|
+
| `imageAlt` | `string` | `undefined` | Alt text for the card image |
|
|
241
|
+
| `hasCheckbox` | `boolean` | `true` | Whether to show the checkbox icon inside the card |
|
|
242
|
+
| `emphasis` | `boolean` | `false` | Applies emphasis styling (blue icon when checked) |
|
|
243
|
+
| `disabled` | `boolean` | `false` | Disables the card |
|
|
244
|
+
| `error` | `boolean` | `false` | Shows error state (red checkbox icon) |
|
|
245
|
+
| `required` | `boolean` | `false` | Marks the input as required |
|
|
246
|
+
| `tooltip` | `string` | `undefined` | Text to display in the info tooltip |
|
|
247
|
+
| `tooltipStatus` | `FzTooltipStatus` | `'neutral'` | Tooltip color/status variant |
|
|
248
|
+
| `name` | `string` | `undefined` | Input `name` attribute for form submission |
|
|
249
|
+
|
|
250
|
+
> **Note:** When `variant` is `'vertical'`, the `imageUrl` prop is required since the vertical layout is designed around a full-width image.
|
|
251
|
+
|
|
252
|
+
#### v-model
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
v-model: (string | number | boolean)[]
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
#### Variant Layouts
|
|
259
|
+
|
|
260
|
+
- **Horizontal** (default) — Checkbox icon, optional thumbnail image (58×58), and text are arranged in a row. Compact layout suited for lists.
|
|
261
|
+
- **Vertical** — Full-width 16:9 image on top, checkbox icon overlaid, and text below. Designed for visually rich selections.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
194
265
|
## Advanced Usage
|
|
195
266
|
|
|
196
267
|
### With Error Validation
|
|
@@ -368,6 +439,67 @@ const dayOptions = [
|
|
|
368
439
|
</template>
|
|
369
440
|
```
|
|
370
441
|
|
|
442
|
+
### Checkbox Card — Vertical with Image
|
|
443
|
+
|
|
444
|
+
```vue
|
|
445
|
+
<script setup lang="ts">
|
|
446
|
+
import { ref } from 'vue';
|
|
447
|
+
import { FzCheckboxCard } from '@fiscozen/checkbox';
|
|
448
|
+
|
|
449
|
+
const selected = ref<(string | number)[]>([]);
|
|
450
|
+
</script>
|
|
451
|
+
|
|
452
|
+
<template>
|
|
453
|
+
<div style="display: flex; gap: 16px; max-width: 720px;">
|
|
454
|
+
<FzCheckboxCard
|
|
455
|
+
v-model="selected"
|
|
456
|
+
label="plan-basic"
|
|
457
|
+
value="basic"
|
|
458
|
+
variant="vertical"
|
|
459
|
+
image-url="/images/basic-plan.jpg"
|
|
460
|
+
image-alt="Basic plan illustration"
|
|
461
|
+
title="Basic Plan"
|
|
462
|
+
subtitle="For individuals getting started"
|
|
463
|
+
tooltip="Includes 5 GB storage"
|
|
464
|
+
/>
|
|
465
|
+
|
|
466
|
+
<FzCheckboxCard
|
|
467
|
+
v-model="selected"
|
|
468
|
+
label="plan-pro"
|
|
469
|
+
value="pro"
|
|
470
|
+
variant="vertical"
|
|
471
|
+
image-url="/images/pro-plan.jpg"
|
|
472
|
+
image-alt="Pro plan illustration"
|
|
473
|
+
title="Pro Plan"
|
|
474
|
+
subtitle="For growing teams"
|
|
475
|
+
tooltip="Includes 50 GB storage"
|
|
476
|
+
/>
|
|
477
|
+
</div>
|
|
478
|
+
</template>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Checkbox Card — Without Checkbox Icon
|
|
482
|
+
|
|
483
|
+
```vue
|
|
484
|
+
<script setup lang="ts">
|
|
485
|
+
import { ref } from 'vue';
|
|
486
|
+
import { FzCheckboxCard } from '@fiscozen/checkbox';
|
|
487
|
+
|
|
488
|
+
const selected = ref<(string | number)[]>([]);
|
|
489
|
+
</script>
|
|
490
|
+
|
|
491
|
+
<template>
|
|
492
|
+
<FzCheckboxCard
|
|
493
|
+
v-model="selected"
|
|
494
|
+
label="option"
|
|
495
|
+
value="option"
|
|
496
|
+
title="Selectable Card"
|
|
497
|
+
subtitle="The card border highlights on selection"
|
|
498
|
+
:has-checkbox="false"
|
|
499
|
+
/>
|
|
500
|
+
</template>
|
|
501
|
+
```
|
|
502
|
+
|
|
371
503
|
### Disabled State
|
|
372
504
|
|
|
373
505
|
```vue
|
|
@@ -458,6 +590,7 @@ This package is written in TypeScript and provides full type definitions.
|
|
|
458
590
|
import type {
|
|
459
591
|
FzCheckboxProps,
|
|
460
592
|
FzCheckboxGroupProps,
|
|
593
|
+
FzCheckboxCardProps,
|
|
461
594
|
ParentCheckbox,
|
|
462
595
|
ChildCheckbox
|
|
463
596
|
} from '@fiscozen/checkbox';
|
|
@@ -473,6 +606,15 @@ const options: ParentCheckbox[] = [
|
|
|
473
606
|
]
|
|
474
607
|
}
|
|
475
608
|
];
|
|
609
|
+
|
|
610
|
+
// Example: Typed card props (vertical variant requires imageUrl)
|
|
611
|
+
const verticalCard: FzCheckboxCardProps = {
|
|
612
|
+
label: 'card',
|
|
613
|
+
title: 'Card Title',
|
|
614
|
+
subtitle: 'Description',
|
|
615
|
+
variant: 'vertical',
|
|
616
|
+
imageUrl: '/images/card.jpg',
|
|
617
|
+
};
|
|
476
618
|
```
|
|
477
619
|
|
|
478
620
|
## Accessibility Features
|
|
@@ -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
|
+
// Selecter 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);
|