@simple-reporting/base 1.0.38 → 1.0.40

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 (100) hide show
  1. package/dev/package.json +1 -1
  2. package/dev/src/App.vue +1 -0
  3. package/dev/src/assets/scss/components/note/accordion.scss +4 -1
  4. package/dev/src/assets/scss/editor.scss +1 -0
  5. package/dev/src/assets/scss/general.scss +18 -0
  6. package/dev/src/assets/scss/margins.scss +4 -2
  7. package/dev/src/entries/pdf.ts +5 -0
  8. package/dev/srl.config.json +60 -6
  9. package/livingdocs/010.Titles/010.title-h1/scss/_spacing-variations.scss +0 -2
  10. package/livingdocs/010.Titles/010.title-h1/scss/web.scss +7 -0
  11. package/livingdocs/010.Titles/020.title-h2/scss/_spacing-variations.scss +0 -2
  12. package/livingdocs/010.Titles/020.title-h2/scss/general.scss +10 -0
  13. package/livingdocs/010.Titles/020.title-h2/title-h2.html +5 -5
  14. package/livingdocs/010.Titles/030.title-h3/scss/_spacing-variations.scss +0 -2
  15. package/livingdocs/010.Titles/030.title-h3/title-h3.html +5 -5
  16. package/livingdocs/010.Titles/040.title-h4/scss/_spacing-variations.scss +0 -2
  17. package/livingdocs/010.Titles/040.title-h4/title-h4.html +5 -5
  18. package/livingdocs/010.Titles/050.title-h5/scss/_spacing-variations.scss +0 -2
  19. package/livingdocs/010.Titles/060.title-h6/scss/_spacing-variations.scss +0 -2
  20. package/livingdocs/020.Text/020.paragraph/scss/_spacing-variations.scss +1 -2
  21. package/livingdocs/020.Text/020.paragraph/scss/general.scss +4 -0
  22. package/livingdocs/020.Text/040.link/scss/general.scss +8 -0
  23. package/livingdocs/020.Text/060.quote-with-portrait/scss/_spacing-variations.scss +1 -2
  24. package/livingdocs/020.Text/060.quote-with-portrait/scss/general.scss +12 -0
  25. package/livingdocs/020.Text/060.quote-with-portrait/scss/web.scss +7 -0
  26. package/livingdocs/020.Text/070.footnote-container/scss/_spacing-variations.scss +0 -2
  27. package/livingdocs/020.Text/080.footnote-item/scss/word.scss +8 -0
  28. package/livingdocs/030.Lists/010.unordered-list/scss/_spacing-variations.scss +0 -2
  29. package/livingdocs/040.Media/010.table/scss/_spacing-variations.scss +0 -2
  30. package/livingdocs/040.Media/020.image/scss/_spacing-variations.scss +0 -2
  31. package/livingdocs/040.Media/020.image/scss/web.scss +19 -5
  32. package/livingdocs/040.Media/030.video/ld-conf.json +1 -6
  33. package/livingdocs/040.Media/030.video/properties.json +7 -0
  34. package/livingdocs/040.Media/030.video/scss/_spacing-variations.scss +0 -2
  35. package/livingdocs/040.Media/030.video/scss/editor.scss +10 -0
  36. package/livingdocs/040.Media/030.video/scss/general.scss +6 -1
  37. package/livingdocs/040.Media/030.video/scss/pdf.scss +8 -0
  38. package/livingdocs/040.Media/030.video/video.html +27 -30
  39. package/livingdocs/040.Media/030.video/video.vue +71 -32
  40. package/livingdocs/060.Buttons/020.button/scss/general.scss +2 -2
  41. package/livingdocs/070.Container/010.aside-content-container/scss/pdf.scss +15 -0
  42. package/livingdocs/070.Container/010.aside-content-container/scss/web.scss +4 -0
  43. package/livingdocs/070.Container/020.columns-container/scss/app.scss +1 -0
  44. package/livingdocs/070.Container/020.columns-container/scss/editor.scss +1 -0
  45. package/livingdocs/070.Container/020.columns-container/scss/general.scss +5 -0
  46. package/livingdocs/070.Container/020.columns-container/scss/pdf.scss +8 -0
  47. package/livingdocs/070.Container/020.columns-container/scss/web.scss +1 -0
  48. package/livingdocs/070.Container/020.columns-container/scss/word.scss +1 -0
  49. package/livingdocs/070.Container/020.columns-container/scss/xbrl.scss +2 -0
  50. package/livingdocs/080.CV/010.cv/scss/editor.scss +10 -1
  51. package/livingdocs/080.CV/010.cv/scss/web.scss +1 -1
  52. package/livingdocs/080.CV/020.cv-time-span/scss/_spacing-variations.scss +0 -2
  53. package/livingdocs/100.Misc/010.anchor/scss/editor.scss +4 -0
  54. package/livingdocs/100.Misc/020.accordion/accordion.html +9 -9
  55. package/livingdocs/110.PDF/010.pdf-pagebreak/scss/editor.scss +1 -0
  56. package/livingdocs/110.PDF/021.pdf-columnbreak/scss/editor.scss +1 -0
  57. package/livingdocs/110.PDF/070.pdf-cover/properties.json +2 -2
  58. package/livingdocs/110.PDF/070.pdf-cover/scss/general.scss +3 -0
  59. package/livingdocs/110.PDF/100.pdf-toc-item/scss/general.scss +6 -8
  60. package/livingdocs/120.Startpage/010.hero-video/hero-video.html +10 -0
  61. package/livingdocs/120.Startpage/010.hero-video/ld-conf.json +5 -0
  62. package/livingdocs/120.Startpage/010.hero-video/properties.json +1 -0
  63. package/livingdocs/120.Startpage/010.hero-video/scss/_spacing-variations.scss +3 -0
  64. package/livingdocs/120.Startpage/010.hero-video/scss/app.scss +2 -0
  65. package/livingdocs/120.Startpage/010.hero-video/scss/editor.scss +9 -0
  66. package/livingdocs/120.Startpage/010.hero-video/scss/web.scss +78 -0
  67. package/livingdocs/120.Startpage/020.teaser-title-image-quote/ld-conf.json +18 -0
  68. package/livingdocs/120.Startpage/020.teaser-title-image-quote/properties.json +1 -0
  69. package/livingdocs/120.Startpage/020.teaser-title-image-quote/scss/_spacing-variations.scss +4 -0
  70. package/livingdocs/120.Startpage/020.teaser-title-image-quote/scss/app.scss +2 -0
  71. package/livingdocs/120.Startpage/020.teaser-title-image-quote/scss/editor.scss +22 -0
  72. package/livingdocs/120.Startpage/020.teaser-title-image-quote/scss/web.scss +41 -0
  73. package/livingdocs/120.Startpage/020.teaser-title-image-quote/teaser-title-image-quote.html +45 -0
  74. package/livingdocs/120.Startpage/030.teaser-quote/ld-conf.json +25 -0
  75. package/livingdocs/120.Startpage/030.teaser-quote/properties.json +1 -0
  76. package/livingdocs/120.Startpage/030.teaser-quote/scss/_spacing-variations.scss +3 -0
  77. package/livingdocs/120.Startpage/030.teaser-quote/scss/app.scss +2 -0
  78. package/livingdocs/120.Startpage/030.teaser-quote/scss/editor.scss +6 -0
  79. package/livingdocs/120.Startpage/030.teaser-quote/scss/web.scss +126 -0
  80. package/livingdocs/120.Startpage/030.teaser-quote/teaser-quote.html +48 -0
  81. package/livingdocs/120.Startpage/040.teaser/ld-conf.json +23 -0
  82. package/livingdocs/120.Startpage/040.teaser/scss/_spacing-variations.scss +3 -0
  83. package/livingdocs/120.Startpage/040.teaser/scss/app.scss +2 -0
  84. package/livingdocs/120.Startpage/040.teaser/scss/editor.scss +6 -0
  85. package/livingdocs/120.Startpage/040.teaser/scss/web.scss +73 -0
  86. package/livingdocs/120.Startpage/040.teaser/teaser.html +34 -0
  87. package/livingdocs/130.Hosting_Components/010.download-center/download-center.html +5 -1
  88. package/livingdocs/130.Hosting_Components/020.search/scss/web.scss +7 -44
  89. package/livingdocs/130.Hosting_Components/020.search/search.html +7 -1
  90. package/livingdocs/130.Hosting_Components/020.search/search.vue +1 -1
  91. package/livingdocs/130.Hosting_Components/020.search/searchHighlightOnTarget.vue +246 -0
  92. package/livingdocs/999.Properties/font-color/properties.json +15 -0
  93. package/package.json +1 -1
  94. package/scripts/config.js +2 -0
  95. package/scss/spacer/mixins.scss +64 -12
  96. package/srl/srl/fa/index.scss +2 -2
  97. package/srl/srl/fa/source-free.scss +1 -4
  98. package/srl/srl/pdf/PDFNestedContainers.ts +110 -0
  99. package/srl/srl/pdf/PDFNotes.ts +4 -2
  100. package/srl/srl/pdf/PDFSetPageNumbers.ts +3 -1
@@ -0,0 +1,246 @@
1
+ <script setup lang="ts">
2
+ import { nextTick, onBeforeUnmount, onMounted, watch } from 'vue'
3
+ import { useRoute } from 'vue-router'
4
+
5
+ const props = withDefaults(defineProps<{
6
+ rootSelector?: string
7
+ highlightClass?: string
8
+ queryParam?: string
9
+ }>(), {
10
+ rootSelector: '#srl-page-main',
11
+ highlightClass: 'searchTarget',
12
+ queryParam: 'searchTarget'
13
+ })
14
+
15
+ const route = useRoute()
16
+
17
+ let mutationObserver: MutationObserver | null = null
18
+ let highlightTimeout: ReturnType<typeof setTimeout> | null = null
19
+ let isApplyingHighlight = false
20
+ let hasScrolledToHighlight = false
21
+
22
+ function getRootElement(): HTMLElement | null {
23
+ return document.querySelector(props.rootSelector)
24
+ }
25
+
26
+ function escapeRegExp(value: string): string {
27
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
28
+ }
29
+
30
+ function removeHighlights(root: HTMLElement): void {
31
+ const highlights = root.querySelectorAll(`span.${props.highlightClass}`)
32
+
33
+ highlights.forEach((highlight) => {
34
+ const parent = highlight.parentNode
35
+ if (!parent) return
36
+
37
+ parent.replaceChild(document.createTextNode(highlight.textContent ?? ''), highlight)
38
+ parent.normalize()
39
+ })
40
+ }
41
+
42
+ function highlightTextNodes(root: HTMLElement, term: string): number {
43
+ const safeTermPattern = escapeRegExp(term)
44
+ const regex = new RegExp(`(${safeTermPattern})`, 'gi')
45
+
46
+ const walker = document.createTreeWalker(
47
+ root,
48
+ NodeFilter.SHOW_TEXT,
49
+ {
50
+ acceptNode(node) {
51
+ const parent = node.parentElement
52
+ if (!parent) return NodeFilter.FILTER_REJECT
53
+
54
+ const tag = parent.tagName.toLowerCase()
55
+ if (
56
+ tag === 'script' ||
57
+ tag === 'style' ||
58
+ tag === 'noscript' ||
59
+ tag === 'textarea'
60
+ ) {
61
+ return NodeFilter.FILTER_REJECT
62
+ }
63
+
64
+ if (parent.closest(`.${props.highlightClass}`)) {
65
+ return NodeFilter.FILTER_REJECT
66
+ }
67
+
68
+ const text = node.textContent
69
+ if (!text || !text.trim()) return NodeFilter.FILTER_SKIP
70
+
71
+ regex.lastIndex = 0
72
+ if (!regex.test(text)) return NodeFilter.FILTER_SKIP
73
+
74
+ return NodeFilter.FILTER_ACCEPT
75
+ }
76
+ }
77
+ )
78
+
79
+ const textNodes: Text[] = []
80
+ let node: Node | null = null
81
+
82
+ while ((node = walker.nextNode())) {
83
+ textNodes.push(node as Text)
84
+ }
85
+
86
+ let hitCount = 0
87
+
88
+ for (const textNode of textNodes) {
89
+ const text = textNode.textContent ?? ''
90
+ regex.lastIndex = 0
91
+
92
+ const matches = [...text.matchAll(regex)]
93
+ if (!matches.length) continue
94
+
95
+ const fragment = document.createDocumentFragment()
96
+ let lastIndex = 0
97
+
98
+ for (const match of matches) {
99
+ const matchText = match[0]
100
+ const index = match.index ?? 0
101
+
102
+ if (index > lastIndex) {
103
+ fragment.appendChild(document.createTextNode(text.slice(lastIndex, index)))
104
+ }
105
+
106
+ const span = document.createElement('span')
107
+ span.className = props.highlightClass
108
+ span.textContent = matchText
109
+ fragment.appendChild(span)
110
+
111
+ lastIndex = index + matchText.length
112
+ hitCount++
113
+ }
114
+
115
+ if (lastIndex < text.length) {
116
+ fragment.appendChild(document.createTextNode(text.slice(lastIndex)))
117
+ }
118
+
119
+ textNode.parentNode?.replaceChild(fragment, textNode)
120
+ }
121
+
122
+ return hitCount
123
+ }
124
+
125
+ function scrollToFirstHighlight(root: HTMLElement): void {
126
+ const firstHighlight = root.querySelector<HTMLElement>(`.${props.highlightClass}`)
127
+ if (!firstHighlight) return
128
+
129
+ firstHighlight.scrollIntoView({
130
+ behavior: 'smooth',
131
+ block: 'center',
132
+ inline: 'nearest'
133
+ })
134
+ }
135
+
136
+ function stopObserver(): void {
137
+ mutationObserver?.disconnect()
138
+ }
139
+
140
+ function startObserver(): void {
141
+ const root = getRootElement()
142
+ if (!root) return
143
+
144
+ stopObserver()
145
+
146
+ mutationObserver = new MutationObserver(() => {
147
+ if (isApplyingHighlight) return
148
+ scheduleHighlight()
149
+ })
150
+
151
+ mutationObserver.observe(root, {
152
+ childList: true,
153
+ subtree: true,
154
+ characterData: true
155
+ })
156
+ }
157
+
158
+ function scheduleHighlight(): void {
159
+ if (highlightTimeout) {
160
+ clearTimeout(highlightTimeout)
161
+ }
162
+
163
+ highlightTimeout = setTimeout(() => {
164
+ void applySearchHighlight()
165
+ }, 0)
166
+ }
167
+
168
+ async function applySearchHighlight(): Promise<void> {
169
+ const root = getRootElement()
170
+ if (!root || isApplyingHighlight) return
171
+
172
+ const rawSearchTarget = route.query[props.queryParam]
173
+ const searchTarget =
174
+ typeof rawSearchTarget === 'string'
175
+ ? decodeURIComponent(rawSearchTarget).trim()
176
+ : ''
177
+
178
+ isApplyingHighlight = true
179
+ stopObserver()
180
+
181
+ try {
182
+ removeHighlights(root)
183
+
184
+ if (!searchTarget) {
185
+ hasScrolledToHighlight = false
186
+ return
187
+ }
188
+
189
+ await nextTick()
190
+
191
+ const hitCount = highlightTextNodes(root, searchTarget)
192
+
193
+ if (hitCount > 0 && !hasScrolledToHighlight) {
194
+ await nextTick()
195
+ scrollToFirstHighlight(root)
196
+ hasScrolledToHighlight = true
197
+ }
198
+ } finally {
199
+ isApplyingHighlight = false
200
+ startObserver()
201
+ }
202
+ }
203
+
204
+ onMounted(async () => {
205
+ await nextTick()
206
+ startObserver()
207
+ await applySearchHighlight()
208
+ })
209
+
210
+ watch(
211
+ () => route.fullPath,
212
+ async () => {
213
+ hasScrolledToHighlight = false
214
+ await nextTick()
215
+ scheduleHighlight()
216
+ }
217
+ )
218
+
219
+ watch(
220
+ () => props.rootSelector,
221
+ async () => {
222
+ hasScrolledToHighlight = false
223
+ await nextTick()
224
+ startObserver()
225
+ scheduleHighlight()
226
+ }
227
+ )
228
+
229
+ onBeforeUnmount(() => {
230
+ stopObserver()
231
+
232
+ if (highlightTimeout) {
233
+ clearTimeout(highlightTimeout)
234
+ }
235
+ })
236
+ </script>
237
+
238
+ <template />
239
+
240
+ <style lang="scss">
241
+ @use "srl";
242
+ .searchTarget {
243
+ background-color: srl.colors-primary-1000();
244
+ color: srl.colors-white-1000();
245
+ }
246
+ </style>
@@ -0,0 +1,15 @@
1
+ {
2
+ "font-color": {
3
+ "label": "Font color",
4
+ "type": "select",
5
+ "options": [
6
+ {
7
+ "caption": "Default"
8
+ },
9
+ {
10
+ "caption": "white",
11
+ "value": "srl-color-white-1000"
12
+ }
13
+ ]
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simple-reporting/base",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "description": "Manage srl templates, build and publish",
5
5
  "repository": {
6
6
  "url": "https://github.com/mmssolutionsio/simple-reporting-library"
package/scripts/config.js CHANGED
@@ -35,6 +35,8 @@ export const baseComponentsToInstall = [
35
35
  '110.PDF/080.pdf-toc-page',
36
36
  '110.PDF/090.pdf-toc-item-title',
37
37
  '110.PDF/100.pdf-toc-item',
38
+ '130.Hosting_Components/010.download-center',
39
+ '130.Hosting_Components/020.search',
38
40
  '999.Properties/alignment',
39
41
  '999.Properties/hide-quote-characters',
40
42
  '999.Properties/icon',
@@ -9,27 +9,53 @@
9
9
  }
10
10
 
11
11
  @mixin margin-block($spacer) {
12
- margin-block: functions.get($spacer);
12
+ @if system.$build == "word" {
13
+ margin-top: functions.get($spacer);
14
+ margin-bottom: functions.get($spacer);
15
+ } @else {
16
+ margin-block: functions.get($spacer);
17
+ }
13
18
  }
14
19
 
15
20
  @mixin margin-inline($spacer) {
16
- margin-inline: functions.get($spacer);
21
+ @if system.$build == "word" {
22
+ margin-left: functions.get($spacer);
23
+ margin-right: functions.get($spacer);
24
+ } @else {
25
+ margin-inline: functions.get($spacer);
26
+ }
17
27
  }
18
28
 
19
29
  @mixin margin-top($spacer) {
20
- margin-block-start: functions.get($spacer);
30
+ @if system.$build == "word" {
31
+ margin-top: functions.get($spacer);
32
+ } @else {
33
+ margin-block-start: functions.get($spacer);
34
+ }
21
35
  }
22
36
 
23
37
  @mixin margin-right($spacer) {
24
- margin-inline-end: functions.get($spacer);
38
+ @if system.$build == "word" {
39
+ margin-right: functions.get($spacer);
40
+ } @else {
41
+ margin-inline-end: functions.get($spacer);
42
+ }
25
43
  }
26
44
 
27
45
  @mixin margin-bottom($spacer) {
28
- margin-block-end: functions.get($spacer);
46
+ @if system.$build == "word" {
47
+ margin-bottom: functions.get($spacer);
48
+ } @else {
49
+ margin-block-end: functions.get($spacer);
50
+ }
29
51
  }
30
52
 
31
53
  @mixin margin-left($spacer) {
32
- margin-inline-start: functions.get($spacer);
54
+ @if system.$build == "word" {
55
+ margin-left: functions.get($spacer);
56
+ } @else {
57
+ margin-inline-start: functions.get($spacer);
58
+ }
33
59
  }
34
60
 
35
61
  @mixin padding($spacer) {
@@ -37,27 +63,53 @@
37
63
  }
38
64
 
39
65
  @mixin padding-block($spacer) {
40
- padding-block: functions.get($spacer);
66
+ @if system.$build == "word" {
67
+ padding-top: functions.get($spacer);
68
+ padding-bottom: functions.get($spacer);
69
+ } @else {
70
+ padding-block: functions.get($spacer);
71
+ }
41
72
  }
42
73
 
43
74
  @mixin padding-inline($spacer) {
44
- padding-inline: functions.get($spacer);
75
+ @if system.$build == "word" {
76
+ padding-left: functions.get($spacer);
77
+ padding-right: functions.get($spacer);
78
+ } @else {
79
+ padding-inline: functions.get($spacer);
80
+ }
45
81
  }
46
82
 
47
83
  @mixin padding-top($spacer) {
48
- padding-block-start: functions.get($spacer);
84
+ @if system.$build == "word" {
85
+ padding-top: functions.get($spacer);
86
+ } @else {
87
+ padding-block-start: functions.get($spacer);
88
+ }
49
89
  }
50
90
 
51
91
  @mixin padding-right($spacer) {
52
- padding-inline-end: functions.get($spacer);
92
+ @if system.$build == "word" {
93
+ padding-right: functions.get($spacer);
94
+ } @else {
95
+ padding-inline-end: functions.get($spacer);
96
+ }
53
97
  }
54
98
 
55
99
  @mixin padding-bottom($spacer) {
56
- padding-block-end: functions.get($spacer);
100
+ @if system.$build == "word" {
101
+ padding-bottom: functions.get($spacer);
102
+ } @else {
103
+ padding-block-end: functions.get($spacer);
104
+ }
57
105
  }
58
106
 
59
107
  @mixin padding-left($spacer) {
60
- padding-inline-start: functions.get($spacer);
108
+ @if system.$build == "word" {
109
+ padding-left: functions.get($spacer);
110
+ } @else {
111
+ padding-inline-start: functions.get($spacer);
112
+ }
61
113
  }
62
114
 
63
115
  @mixin gap($spacer) {
@@ -63,7 +63,7 @@
63
63
  @return 900;
64
64
  }
65
65
 
66
- @mixin content($icon, $style: solid) {
66
+ @mixin content($icon, $style: v.$default-style) {
67
67
  @if not map.has-key(v.$brand-icons, $icon) and not map.has-key(v.$icons, $icon) {
68
68
  @debug "Icon "#{$icon}" not found in Font Awesome 7.";
69
69
  } @else {
@@ -88,7 +88,7 @@
88
88
  }
89
89
  }
90
90
 
91
- @mixin content-before($icon, $style: solid) {
91
+ @mixin content-before($icon, $style: v.$default-style) {
92
92
  @if not map.has-key(v.$brand-icons, $icon) and not map.has-key(v.$icons, $icon) {
93
93
  @debug "Icon "#{$icon}" not found in Font Awesome 7.";
94
94
  } @else {
@@ -8,7 +8,4 @@
8
8
  @forward "@fortawesome/fontawesome-free/scss/variables";
9
9
  @forward "@fortawesome/fontawesome-free/scss/mixins";
10
10
  $type: free;
11
- $default-style: light;
12
- @if map.has-key(meta.$meta, icon, default-style) {
13
- $default-style: map.get(meta.$meta, icon, default-style);
14
- }
11
+ $default-style: solid;
@@ -0,0 +1,110 @@
1
+ export interface PDFNestedContainerMarkerConfig {
2
+ selector?: string
3
+ }
4
+
5
+ export class PDFNestedContainers {
6
+ private containers: NodeListOf<HTMLElement>
7
+
8
+ private excludedClasses = ['srl-grid', 'srl-linkable', 'srl-nested-container']
9
+
10
+ private wrapperClasses = ['srl-grid']
11
+
12
+ constructor(config: PDFNestedContainerMarkerConfig = {}) {
13
+ const { selector = '.srl-nested-container' } = config
14
+
15
+ this.containers = document.querySelectorAll(selector)
16
+
17
+ if (this.containers.length === 0) {
18
+ return
19
+ }
20
+
21
+ this.markContainers()
22
+ }
23
+
24
+ private markContainers(): void {
25
+ this.containers.forEach((container) => {
26
+ const firstElement = this.findEdgeElement(container, 'first')
27
+ const lastElement = this.findEdgeElement(container, 'last')
28
+
29
+ const firstClass = firstElement ? this.getRelevantClass(firstElement) : null
30
+ const lastClass = lastElement ? this.getRelevantClass(lastElement) : null
31
+
32
+ if (firstClass) {
33
+ container.classList.add(`${firstClass}-first`)
34
+ }
35
+
36
+ if (lastClass) {
37
+ container.classList.add(`${lastClass}-last`)
38
+ }
39
+ })
40
+ }
41
+
42
+ private findEdgeElement(root: HTMLElement, direction: 'first' | 'last'): HTMLElement | null {
43
+ const children = Array.from(root.children) as HTMLElement[]
44
+
45
+ const orderedChildren = direction === 'first' ? children : [...children].reverse()
46
+
47
+ for (const child of orderedChildren) {
48
+ if (this.isLayoutWrapper(child)) {
49
+ const nestedElement = this.findEdgeElement(child, direction)
50
+
51
+ if (nestedElement) {
52
+ return nestedElement
53
+ }
54
+
55
+ continue
56
+ }
57
+
58
+ const relevantClass = this.getRelevantClass(child)
59
+
60
+ if (relevantClass) {
61
+ return child
62
+ }
63
+
64
+ const nestedElement = this.findEdgeElement(child, direction)
65
+
66
+ if (nestedElement) {
67
+ return nestedElement
68
+ }
69
+ }
70
+
71
+ return null
72
+ }
73
+
74
+ private isLayoutWrapper(el: HTMLElement): boolean {
75
+ const classes = Array.from(el.classList)
76
+
77
+ if (classes.some((cls) => this.wrapperClasses.includes(cls))) {
78
+ return true
79
+ }
80
+
81
+ if (classes.some((cls) => cls.includes('__'))) {
82
+ return true
83
+ }
84
+
85
+ if (el.hasAttribute('doc-container') && !this.getRelevantClass(el)) {
86
+ return true
87
+ }
88
+
89
+ return false
90
+ }
91
+
92
+ private getRelevantClass(el: HTMLElement): string | null {
93
+ const classes = Array.from(el.classList)
94
+
95
+ const relevant = classes.find((cls) => {
96
+ if (!cls.startsWith('srl-')) return false
97
+ if (this.excludedClasses.includes(cls)) return false
98
+ if (cls.includes('__')) return false
99
+ if (this.wrapperClasses.includes(cls)) return false
100
+ if (cls.endsWith('-first')) return false
101
+ if (cls.endsWith('-last')) return false
102
+
103
+ return true
104
+ })
105
+
106
+ return relevant || null
107
+ }
108
+ }
109
+
110
+ export default PDFNestedContainers
@@ -2,7 +2,7 @@ export interface PDFNotesConfig {
2
2
  noteClass: string; // kann mehrere Selektoren per Komma enthalten
3
3
  }
4
4
 
5
- export default class PDFNotes {
5
+ export class PDFNotes {
6
6
  private notesArticles: NodeListOf<HTMLElement>;
7
7
  private selectors: string[];
8
8
  private excludedClasses = ['srl-grid', 'srl-linkable'];
@@ -68,4 +68,6 @@ export default class PDFNotes {
68
68
  // last.classList.add(`last-note-${typeKey}`);
69
69
  });
70
70
  }
71
- }
71
+ }
72
+
73
+ export default PDFNotes
@@ -3,7 +3,7 @@ export interface PDFSetPageNumbersConfig {
3
3
  tocItemPageNumberClass: string;
4
4
  }
5
5
 
6
- export default class PDFSetPageNumbers {
6
+ export class PDFSetPageNumbers {
7
7
  private tocItems: NodeListOf<HTMLElement>;
8
8
  private pageNumberClass: string;
9
9
 
@@ -23,3 +23,5 @@ export default class PDFSetPageNumbers {
23
23
  });
24
24
  }
25
25
  }
26
+
27
+ export default PDFSetPageNumbers