@afeefa/vue-app 0.0.308 → 0.0.309
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/.afeefa/package/release/version.txt +1 -1
- package/package.json +1 -1
- package/src-admin/components/StickyHeader.vue +5 -1
- package/src-admin/components/app/AppBarNavigation.vue +108 -0
- package/src-admin/components/app/AppBarTitle.vue +1 -1
- package/src-admin/components/detail/EditableDetailProperty.vue +4 -4
- package/src-admin/config/AdminConfig.js +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.309
|
package/package.json
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
<app-bar-title-container class="appBarTitle flex-grow-1" />
|
|
19
19
|
<app-bar-buttons class="appBarButtons mr-2" />
|
|
20
20
|
</div>
|
|
21
|
+
|
|
22
|
+
<app-bar-navigation v-if="$config.hasAppBarNavigation" />
|
|
21
23
|
</div>
|
|
22
24
|
</template>
|
|
23
25
|
|
|
@@ -25,13 +27,15 @@
|
|
|
25
27
|
import { Component, Vue } from '@a-vue'
|
|
26
28
|
import AppBarButtons from './app/AppBarButtons'
|
|
27
29
|
import AppBarTitleContainer from './app/AppBarTitleContainer'
|
|
30
|
+
import AppBarNavigation from './app/AppBarNavigation'
|
|
28
31
|
import { SidebarEvent } from '@a-admin/events'
|
|
29
32
|
import { sidebarService } from './sidebar/SidebarService'
|
|
30
33
|
|
|
31
34
|
@Component({
|
|
32
35
|
components: {
|
|
33
36
|
AppBarButtons,
|
|
34
|
-
AppBarTitleContainer
|
|
37
|
+
AppBarTitleContainer,
|
|
38
|
+
AppBarNavigation
|
|
35
39
|
}
|
|
36
40
|
})
|
|
37
41
|
export default class StickyHeader extends Vue {
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
id="appBarNavigation"
|
|
4
|
+
class="d-flex align-center gap-1"
|
|
5
|
+
>
|
|
6
|
+
<v-btn
|
|
7
|
+
v-for="link in links"
|
|
8
|
+
:key="link.id"
|
|
9
|
+
class="mt-3 mb-n1"
|
|
10
|
+
x-small
|
|
11
|
+
@click="scrollToSection(link.id)"
|
|
12
|
+
>
|
|
13
|
+
{{ link.label }}
|
|
14
|
+
</v-btn>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script>
|
|
19
|
+
import { Component, Vue } from '@a-vue'
|
|
20
|
+
import { debounce } from '@a-vue/utils/debounce'
|
|
21
|
+
|
|
22
|
+
@Component
|
|
23
|
+
export default class AppBarNavigation extends Vue {
|
|
24
|
+
links = []
|
|
25
|
+
|
|
26
|
+
debounceInitLinks = debounce(this.initLinks, 50) // fire immediately if !value (click clearable-x)
|
|
27
|
+
|
|
28
|
+
mounted () {
|
|
29
|
+
const mutationWatcher = new MutationObserver(mutations => {
|
|
30
|
+
let changed = false
|
|
31
|
+
|
|
32
|
+
for (const m of mutations) {
|
|
33
|
+
// Neue Nodes
|
|
34
|
+
for (const node of m.addedNodes) {
|
|
35
|
+
if (!(node instanceof HTMLElement)) continue
|
|
36
|
+
if (node.matches('.collapsible-section') || node.querySelector('.collapsible-section')) {
|
|
37
|
+
changed = true
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Entfernte Nodes
|
|
42
|
+
for (const node of m.removedNodes) {
|
|
43
|
+
if (!(node instanceof HTMLElement)) continue
|
|
44
|
+
if (node.matches('.collapsible-section') || node.querySelector('.collapsible-section')) {
|
|
45
|
+
changed = true
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (changed) {
|
|
51
|
+
this.debounceInitLinks()
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
mutationWatcher.observe(document.body, { childList: true, subtree: true })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
initLinks () {
|
|
59
|
+
const sections = document.querySelectorAll('.collapsible-section')
|
|
60
|
+
console.log(Array.from(sections).map(s => s.getAttribute('label')))
|
|
61
|
+
this.links = Array.from(sections).map(s => {
|
|
62
|
+
const label = s.getAttribute('label')
|
|
63
|
+
const slug = label
|
|
64
|
+
.toLowerCase()
|
|
65
|
+
.replace(/[^\w]+/g, '-') // Sonderzeichen -> Bindestriche
|
|
66
|
+
.replace(/^-+|-+$/g, '') // führende/trailing -
|
|
67
|
+
.substring(0, 50) // Sicherheitslimit
|
|
68
|
+
|
|
69
|
+
s.id = slug
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
id: s.id,
|
|
73
|
+
label,
|
|
74
|
+
slug
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
scrollToSection (id) {
|
|
80
|
+
const el = document.querySelector(`#${id}`)
|
|
81
|
+
if (!el) return
|
|
82
|
+
|
|
83
|
+
// Scroll-Container auswählen (kann auch this.$refs.scrollContainer sein)
|
|
84
|
+
const container = document.querySelector('#v-main')
|
|
85
|
+
if (!container) return
|
|
86
|
+
|
|
87
|
+
// Abstand in Pixeln (4rem ≈ 64px)
|
|
88
|
+
const offset = 140
|
|
89
|
+
|
|
90
|
+
// Berechne die Scroll-Position relativ zum Container
|
|
91
|
+
const containerTop = container.getBoundingClientRect().top
|
|
92
|
+
const elementTop = el.getBoundingClientRect().top
|
|
93
|
+
|
|
94
|
+
const scrollTarget = container.scrollTop + (elementTop - containerTop) - offset
|
|
95
|
+
|
|
96
|
+
container.scrollTo({
|
|
97
|
+
top: scrollTarget,
|
|
98
|
+
behavior: 'smooth'
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// // Scroll sanft zur Section
|
|
102
|
+
// el.scrollIntoView({
|
|
103
|
+
// behavior: 'smooth',
|
|
104
|
+
// block: 'start'
|
|
105
|
+
// })
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<template #actionButton>
|
|
7
7
|
<a-icon
|
|
8
8
|
v-if="buttonEdit"
|
|
9
|
-
size="1.
|
|
10
|
-
class="contextButton
|
|
9
|
+
size="1.6rem"
|
|
10
|
+
class="contextButton"
|
|
11
11
|
title="Bearbeiten"
|
|
12
12
|
@click="openModal"
|
|
13
13
|
>
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
<a-icon
|
|
18
18
|
v-if="buttonAdd"
|
|
19
|
-
size="1.
|
|
20
|
-
class="contextButton
|
|
19
|
+
size="1.6rem"
|
|
20
|
+
class="contextButton"
|
|
21
21
|
title="Bearbeiten"
|
|
22
22
|
@click="openModal"
|
|
23
23
|
>
|