@pocketprep/ui-kit 3.5.26 → 3.5.28
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/dist/@pocketprep/ui-kit.css +1 -1
- package/dist/@pocketprep/ui-kit.js +1743 -1710
- package/dist/@pocketprep/ui-kit.js.map +1 -1
- package/dist/@pocketprep/ui-kit.umd.cjs +8 -8
- package/dist/@pocketprep/ui-kit.umd.cjs.map +1 -1
- package/lib/components/Forms/Errors.vue +10 -1
- package/lib/utils.ts +60 -11
- package/package.json +1 -1
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
class="uikit-errors__icon"
|
|
12
12
|
type="warning"
|
|
13
13
|
/>
|
|
14
|
+
<div
|
|
15
|
+
v-if="errorHeader"
|
|
16
|
+
class="uikit-errors__error-header"
|
|
17
|
+
v-dark="isDarkMode"
|
|
18
|
+
>
|
|
19
|
+
{{ errorHeader }}
|
|
20
|
+
</div>
|
|
14
21
|
<slot name="errors">
|
|
15
22
|
<div
|
|
16
23
|
v-for="error in filteredErrors"
|
|
@@ -42,6 +49,7 @@ import { dark } from '../../directives'
|
|
|
42
49
|
export default class Errors extends Vue {
|
|
43
50
|
@Prop({ default: () => ([]) }) errors!: string[]
|
|
44
51
|
@Prop({ default: false }) isDarkMode!: boolean
|
|
52
|
+
@Prop() errorHeader!: string
|
|
45
53
|
|
|
46
54
|
get filteredErrors () {
|
|
47
55
|
return [ ...new Set(this.errors) ]
|
|
@@ -92,7 +100,8 @@ export default class Errors extends Vue {
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
&__error
|
|
103
|
+
&__error,
|
|
104
|
+
&__error-header {
|
|
96
105
|
color: $brand-black;
|
|
97
106
|
font-size: 14px;
|
|
98
107
|
line-height: 19px;
|
package/lib/utils.ts
CHANGED
|
@@ -94,17 +94,66 @@ export const highlightKeywordsInText = (params: {
|
|
|
94
94
|
return params.text
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
// Sort keywords by length (longest first) to handle overlapping keywords correctly
|
|
98
|
+
const sortedKeywords = [ ...params.keywordDefinitions ].map(k => k.keyword).sort((a, b) => b.length - a.length)
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
// Create an array for all matching keywords and substrings with their start and end indexes
|
|
101
|
+
const allMatches: { start: number; end: number; keyword: string; keywordMatch: string }[] = []
|
|
102
|
+
|
|
103
|
+
sortedKeywords.forEach((keyword) => {
|
|
104
|
+
const escapedKeyword = escapeRegex(keyword)
|
|
105
|
+
const regex = new RegExp(`\\b${escapedKeyword}\\b`, 'gi')
|
|
106
|
+
|
|
107
|
+
// Use matchAll to get all keyword matches
|
|
108
|
+
// This will include keyword matches that are substrings of other keywords
|
|
109
|
+
// ie: keyword 'stakeholder' will match in a keyword string 'key stakeholder'
|
|
110
|
+
const matches = Array.from(params.text.matchAll(regex))
|
|
111
|
+
matches.forEach((match) => {
|
|
112
|
+
allMatches.push({
|
|
113
|
+
start: match.index,
|
|
114
|
+
end: match.index + match[0].length,
|
|
115
|
+
keyword,
|
|
116
|
+
keywordMatch: match[0],
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Sort all matches by start index
|
|
122
|
+
allMatches.sort((a, b) => a.start - b.start)
|
|
123
|
+
|
|
124
|
+
// Create an array to track which indexes are already highlighted
|
|
125
|
+
const highlightedRanges: { start: number; end: number }[] = []
|
|
126
|
+
|
|
127
|
+
// Create an array to track non-overlapping keyword matches
|
|
128
|
+
const nonOverlappingMatches: { start: number; end: number; keyword: string; keywordMatch: string }[] = []
|
|
129
|
+
// Do not include any matches that are substrings of other matches
|
|
130
|
+
allMatches.forEach((match) => {
|
|
131
|
+
const overlaps = highlightedRanges.some(range =>
|
|
132
|
+
(match.start < range.end && match.end > range.start)
|
|
108
133
|
)
|
|
109
|
-
|
|
134
|
+
|
|
135
|
+
if (!overlaps) {
|
|
136
|
+
nonOverlappingMatches.push(match)
|
|
137
|
+
highlightedRanges.push({ start: match.start, end: match.end })
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Sort by start position again and process in reverse order to maintain positions
|
|
142
|
+
nonOverlappingMatches.sort((a, b) => b.start - a.start)
|
|
143
|
+
|
|
144
|
+
let result = params.text
|
|
145
|
+
|
|
146
|
+
nonOverlappingMatches.forEach((match) => {
|
|
147
|
+
const beforeMatch = result.substring(0, match.start)
|
|
148
|
+
const afterMatch = result.substring(match.end)
|
|
149
|
+
const highlightedMatch = `<span class="keyword-highlight${params.isDarkMode
|
|
150
|
+
? ' keyword-highlight--dark' : ''}" data-location="${
|
|
151
|
+
params.location}" role="button" tabindex="0" aria-label="${
|
|
152
|
+
match.keywordMatch}, Instruction. Click for definition" aria-haspopup="dialog" aria-expanded="false">
|
|
153
|
+
<span style="pointer-events: none;">${match.keywordMatch}</span></span>`
|
|
154
|
+
result = beforeMatch + highlightedMatch + afterMatch
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
return result
|
|
110
158
|
}
|
|
159
|
+
|