@retailcrm/embed-ui 0.9.8 → 0.9.10-alpha.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/.actrc ADDED
@@ -0,0 +1,3 @@
1
+ -P ubuntu-latest=catthehacker/ubuntu:act-latest
2
+ -P ubuntu-24.04=catthehacker/ubuntu:act-latest
3
+ -P ubuntu-22.04=catthehacker/ubuntu:act-latest
@@ -0,0 +1,20 @@
1
+ FROM ubuntu:24.04
2
+
3
+ ARG DEBIAN_FRONTEND=noninteractive
4
+ ARG TARGETARCH
5
+
6
+ RUN apt-get update \
7
+ && apt-get install -y --no-install-recommends ca-certificates curl tar git \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ RUN set -eu; \
11
+ case "${TARGETARCH}" in \
12
+ amd64) ACT_ARCH="x86_64" ;; \
13
+ arm64) ACT_ARCH="arm64" ;; \
14
+ *) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
15
+ esac; \
16
+ curl -fsSL "https://github.com/nektos/act/releases/latest/download/act_Linux_${ACT_ARCH}.tar.gz" \
17
+ | tar -xz -C /usr/local/bin act; \
18
+ chmod +x /usr/local/bin/act
19
+
20
+ ENTRYPOINT ["act"]
package/CHANGELOG.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## [0.9.10-alpha.1](https://github.com/retailcrm/embed-ui/compare/v0.9.9...v0.9.10-alpha.1) (2026-02-17)
5
+
6
+ ### Features
7
+
8
+ * **v1-contexts:** Added fields customer.id, customer.externalId to order/card context ([03e0778](https://github.com/retailcrm/embed-ui/commit/03e077840d7859416a562410c21a1499728fd43a))
9
+ ## [0.9.9](https://github.com/retailcrm/embed-ui/compare/v0.9.8...v0.9.9) (2026-02-12)
10
+
11
+ ### Bug Fixes
12
+
13
+ * **v1-components:** apply vue plugins to storybook worker build ([5054dd7](https://github.com/retailcrm/embed-ui/commit/5054dd700948770b1eecabd37f9ec67a32ab4e55))
4
14
  ## [0.9.8](https://github.com/retailcrm/embed-ui/compare/v0.9.7...v0.9.8) (2026-02-12)
5
15
 
6
16
  ### Features
package/Makefile CHANGED
@@ -1,63 +1,210 @@
1
+ .DEFAULT_GOAL := help
2
+
1
3
  TARGET_HEADER=@echo -e '===== \e[34m' $@ '\e[0m'
2
- YARN=docker-compose run --rm node yarn
4
+ TARGET_OK=@echo -e '\e[32mOK\e[0m'
5
+ COMPOSE=$(shell if command -v docker-compose >/dev/null 2>&1; then echo "docker-compose"; elif command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then echo "docker compose"; fi)
6
+ YARN=$(COMPOSE) run --rm node yarn
7
+
8
+ .PHONY: .require-compose
9
+ .require-compose:
10
+ @if [ -z "$(COMPOSE)" ]; then \
11
+ echo "docker compose is unavailable (need 'docker compose' or 'docker-compose')"; \
12
+ exit 1; \
13
+ fi
3
14
 
4
15
  .PHONY: .yarnrc.yml
5
- .yarnrc.yml: ## Generates yarn configuration
16
+ .yarnrc.yml: ## [Setup][local] Generates yarn configuration
6
17
  $(TARGET_HEADER)
7
18
  cp .yarnrc.dist.yml .yarnrc.yml
19
+ $(TARGET_OK)
8
20
 
9
21
  .PHONY: node_modules
10
- node_modules: package.json yarn.lock ## Installs dependencies
22
+ node_modules: .require-compose package.json yarn.lock ## [Setup][docker][heavy] Installs dependencies
11
23
  $(TARGET_HEADER)
12
- @docker-compose run --rm node yarn install --silent
24
+ @$(COMPOSE) run --rm node yarn install --silent
25
+ $(TARGET_OK)
13
26
 
14
27
  .PHONY: build
15
- build: ## Builds the package
28
+ build: .require-compose ## [Build][docker][heavy] Builds all workspaces
16
29
  $(TARGET_HEADER)
17
30
  $(YARN) workspaces foreach -A --topological-dev run build
31
+ $(TARGET_OK)
18
32
 
19
- .PHONY: build
20
- prepare: ## Builds the package
33
+ .PHONY: prepare
34
+ prepare: .require-compose ## [Build][docker][heavy] Runs prepare in all workspaces
21
35
  $(TARGET_HEADER)
22
36
  $(YARN) workspaces foreach -A --topological-dev run prepare
37
+ $(TARGET_OK)
23
38
 
24
39
  .PHONY: release
25
- release: ## Bumps version and creates tag
40
+ release: .require-compose ## [Release][docker][heavy][network] Bumps version and creates tag
26
41
  $(TARGET_HEADER)
27
42
  ifdef as
28
43
  $(YARN) release:$(as)
29
44
  else
30
45
  $(YARN) release
31
46
  endif
47
+ $(TARGET_OK)
32
48
 
33
49
  .PHONY: tests
34
- tests: ## Runs autotests
50
+ tests: .require-compose ## [Tests][docker] Runs autotests
35
51
  $(TARGET_HEADER)
36
52
  ifdef cli
37
53
  $(YARN) test $(cli) --passWithNoTests
38
54
  else
39
55
  $(YARN) test
40
56
  endif
57
+ $(TARGET_OK)
58
+
59
+ .PHONY: tests-coverage
60
+ tests-coverage: .require-compose ## [Tests][docker][heavy] Runs autotests with coverage report
61
+ $(TARGET_HEADER)
62
+ ifdef cli
63
+ $(YARN) vitest --run --coverage $(cli) --passWithNoTests
64
+ else
65
+ $(YARN) test:coverage
66
+ endif
67
+ $(TARGET_OK)
41
68
 
42
69
  .PHONY: tests-typecheck-contexts
43
- tests-typecheck-contexts: ## Runs typecheck tests (test-d.ts) for v1-contexts
70
+ tests-typecheck-contexts: .require-compose ## [Tests][docker] Runs typecheck tests (test-d.ts) for v1-contexts
44
71
  $(TARGET_HEADER)
45
72
  ifdef cli
46
73
  $(YARN) vitest run -c packages/v1-contexts/vitest.config.ts --typecheck.only --typecheck.checker tsc --typecheck.tsconfig packages/v1-contexts/tsconfig.json $(cli)
47
74
  else
48
75
  $(YARN) vitest run -c packages/v1-contexts/vitest.config.ts --typecheck.only --typecheck.checker tsc --typecheck.tsconfig packages/v1-contexts/tsconfig.json
49
76
  endif
77
+ $(TARGET_OK)
50
78
 
51
79
  .PHONY: tests-typecheck-v1-contexts
52
- tests-typecheck-v1-contexts: tests-typecheck-contexts ## Alias for tests-typecheck-contexts
80
+ tests-typecheck-v1-contexts: tests-typecheck-contexts ## [Tests][alias] Alias for tests-typecheck-contexts
53
81
 
54
82
  .PHONY: tests-typecheck
55
- tests-typecheck: tests-typecheck-contexts ## Runs typecheck tests (currently v1-contexts)
83
+ tests-typecheck: tests-typecheck-contexts ## [Tests][alias] Runs typecheck tests (currently v1-contexts)
84
+
85
+ .PHONY: ci-actionlint
86
+ ci-actionlint: ## [CI][docker] Lints GitHub Actions workflows locally (actionlint binary or docker image)
87
+ $(TARGET_HEADER)
88
+ @if command -v actionlint >/dev/null 2>&1; then \
89
+ actionlint; \
90
+ elif [ -n "$(COMPOSE)" ]; then \
91
+ $(COMPOSE) run --rm actionlint; \
92
+ elif command -v docker >/dev/null 2>&1; then \
93
+ docker run --rm -v "$$(pwd):/repo" -w /repo rhysd/actionlint:latest; \
94
+ else \
95
+ echo "actionlint is not installed and docker/docker-compose is unavailable"; \
96
+ exit 1; \
97
+ fi
98
+ $(TARGET_OK)
99
+
100
+ .PHONY: ci-act-plan
101
+ ci-act-plan: ## [CI][docker] Shows act execution plan for tests workflow without running jobs
102
+ $(TARGET_HEADER)
103
+ @if command -v act >/dev/null 2>&1; then \
104
+ act -n pull_request -W .github/workflows/tests.yml; \
105
+ elif [ -n "$(COMPOSE)" ]; then \
106
+ $(COMPOSE) run --rm --build act -n pull_request -W .github/workflows/tests.yml; \
107
+ else \
108
+ echo "act is not installed and docker compose is unavailable"; \
109
+ exit 1; \
110
+ fi
111
+ $(TARGET_OK)
112
+
113
+ .PHONY: ci-act-tests
114
+ ci-act-tests: ## [CI][docker][heavy][network] Runs tests workflow locally via act
115
+ $(TARGET_HEADER)
116
+ @if command -v act >/dev/null 2>&1; then \
117
+ act pull_request -W .github/workflows/tests.yml -j workflow-lint -j eslint -j tests; \
118
+ elif [ -n "$(COMPOSE)" ]; then \
119
+ $(COMPOSE) run --rm --build act pull_request -W .github/workflows/tests.yml -j workflow-lint -j eslint -j tests; \
120
+ else \
121
+ echo "act is not installed and docker compose is unavailable"; \
122
+ exit 1; \
123
+ fi
124
+ $(TARGET_OK)
56
125
 
57
126
  .PHONY: help
58
- help: ## Calls recipes list
59
- @cat $(MAKEFILE_LIST) | grep -e "^[a-zA-Z_\-]*: *.*## *" | awk '\
60
- BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
127
+ help: ## [General] Shows grouped command help
128
+ @set -eu; \
129
+ if [ -t 1 ] && [ -z "$${CI:-}" ] && [ -z "$${NO_COLOR:-}" ]; then \
130
+ C_HEAD="$$(printf '\033[1;36m')"; \
131
+ C_TGT="$$(printf '\033[36m')"; \
132
+ C_TAG="$$(printf '\033[33m')"; \
133
+ C_WARN="$$(printf '\033[33m')"; \
134
+ C_RST="$$(printf '\033[0m')"; \
135
+ else \
136
+ C_HEAD=''; C_TGT=''; C_TAG=''; C_WARN=''; C_RST=''; \
137
+ fi; \
138
+ FILTER="$$(printf '%s' "$(value filter)" | tr '[:upper:]' '[:lower:]')"; \
139
+ SHOW_INTERNAL="$(value show_internal)"; \
140
+ awk -v filter="$$FILTER" -v show_internal="$$SHOW_INTERNAL" '\
141
+ function trim(s){ sub(/^[ \t\r\n]+/, "", s); sub(/[ \t\r\n]+$$/, "", s); return s } \
142
+ function group_order(g){ \
143
+ if (g=="General") return 1; \
144
+ if (g=="Setup") return 2; \
145
+ if (g=="Build") return 3; \
146
+ if (g=="Tests") return 4; \
147
+ if (g=="CI") return 5; \
148
+ if (g=="Release") return 6; \
149
+ return 99; \
150
+ } \
151
+ /^[a-zA-Z0-9_.-]+:.*##[[:space:]]+/ { \
152
+ target = $$1; sub(/:.*/, "", target); \
153
+ if (show_internal != "1" && target ~ /^\./) next; \
154
+ desc = $$0; sub(/^.*##[[:space:]]*/, "", desc); \
155
+ group = "Other"; tags = ""; \
156
+ if (match(desc, /^\[[^]]+\]/)) { \
157
+ group = substr(desc, 2, RLENGTH - 2); \
158
+ desc = substr(desc, RLENGTH + 1); \
159
+ } \
160
+ while (match(desc, /^[[:space:]]*\[[^]]+\]/)) { \
161
+ sub(/^[[:space:]]*/, "", desc); \
162
+ rb = index(desc, "]"); \
163
+ if (rb == 0) break; \
164
+ tags = tags "[" substr(desc, 2, rb - 2) "]"; \
165
+ desc = substr(desc, rb + 1); \
166
+ } \
167
+ desc = trim(desc); \
168
+ hay = tolower(target " " group " " tags " " desc); \
169
+ if (filter != "" && index(hay, filter) == 0) next; \
170
+ printf "%02d\t%s\t%s\t%s\t%s\n", group_order(group), group, target, tags, desc; \
171
+ } \
172
+ ' $(MAKEFILE_LIST) \
173
+ | sort -t "$$(printf '\t')" -k1,1n -k3,3 \
174
+ | awk -F '\t' -v c_head="$$C_HEAD" -v c_tgt="$$C_TGT" -v c_tag="$$C_TAG" -v c_rst="$$C_RST" '\
175
+ BEGIN { current = ""; count = 0 } \
176
+ { \
177
+ if ($$2 != current) { \
178
+ if (current != "") print ""; \
179
+ printf "%s%s%s\n", c_head, $$2, c_rst; \
180
+ current = $$2; \
181
+ } \
182
+ printf " %s%-30s%s %s", c_tgt, $$3, c_rst, $$5; \
183
+ if ($$4 != "") printf " %s%s%s", c_tag, $$4, c_rst; \
184
+ printf "\n"; \
185
+ count++; \
186
+ } \
187
+ END { if (count == 0) print "No targets matched the current filter." }'; \
188
+ echo ""; \
189
+ printf "%sQuick Start%s\n" "$$C_HEAD" "$$C_RST"; \
190
+ printf " make ci-actionlint\n"; \
191
+ printf " make tests\n"; \
192
+ printf " make tests-coverage\n"; \
193
+ printf " make ci-act-plan\n"; \
194
+ echo ""; \
195
+ printf "%sExamples%s\n" "$$C_HEAD" "$$C_RST"; \
196
+ printf " make help filter=ci\n"; \
197
+ printf " make help show_internal=1\n"; \
198
+ printf " make tests cli='tests/scenarios/customer/phone.test.ts'\n"; \
199
+ printf " make release as=beta\n"; \
200
+ dup_targets="$$(awk -F: '/^[a-zA-Z0-9_.-]+:/ && $$1 != ".PHONY" {print $$1}' $(MAKEFILE_LIST) | sort | uniq -d | tr '\n' ' ')"; \
201
+ dup_phony="$$(awk '/^\.PHONY:[[:space:]]*/ {for (i=2; i<=NF; i++) print $$i}' $(MAKEFILE_LIST) | sort | uniq -d | tr '\n' ' ')"; \
202
+ if [ -n "$$dup_targets$$dup_phony" ]; then \
203
+ echo ""; \
204
+ printf "%sWarnings%s\n" "$$C_WARN" "$$C_RST"; \
205
+ if [ -n "$$dup_targets" ]; then printf " duplicate targets: %s\n" "$$dup_targets"; fi; \
206
+ if [ -n "$$dup_phony" ]; then printf " duplicate .PHONY entries: %s\n" "$$dup_phony"; fi; \
207
+ fi
61
208
 
62
209
  # Colors
63
210
  $(call computable,CC_BLACK,$(shell tput -Txterm setaf 0 2>/dev/null))
@@ -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);