@ulu/frontend-vue 0.1.3-beta.10 → 0.1.3-beta.12
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/frontend-vue.css +1 -1
- package/dist/frontend-vue.js +2918 -2774
- package/dist/types/components/layout/UluDataGrid.vue.d.ts +16 -1
- package/dist/types/components/layout/UluDataGrid.vue.d.ts.map +1 -1
- package/dist/types/components/systems/index.d.ts +4 -0
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchors.vue.d.ts +20 -58
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchors.vue.d.ts.map +1 -1
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchorsHeadlessSection.vue.d.ts +24 -0
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchorsHeadlessSection.vue.d.ts.map +1 -0
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchorsNav.vue.d.ts +17 -13
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchorsNav.vue.d.ts.map +1 -1
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchorsNavAnimated.vue.d.ts +27 -30
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchorsNavAnimated.vue.d.ts.map +1 -1
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchorsSection.vue.d.ts +27 -45
- package/dist/types/components/systems/scroll-anchors/UluScrollAnchorsSection.vue.d.ts.map +1 -1
- package/dist/types/components/systems/scroll-anchors/useScrollAnchorSection.d.ts +9 -0
- package/dist/types/components/systems/scroll-anchors/useScrollAnchorSection.d.ts.map +1 -0
- package/dist/types/components/systems/scroll-anchors/useScrollAnchorSections.d.ts +8 -0
- package/dist/types/components/systems/scroll-anchors/useScrollAnchorSections.d.ts.map +1 -0
- package/dist/types/components/systems/scroll-anchors/useScrollAnchors.d.ts +14 -0
- package/dist/types/components/systems/scroll-anchors/useScrollAnchors.d.ts.map +1 -0
- package/lib/components/_index.scss +1 -0
- package/lib/components/layout/UluDataGrid.vue +51 -16
- package/lib/components/systems/index.js +4 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchors.vue +46 -145
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsHeadlessSection.vue +36 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsNav.vue +18 -16
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsNavAnimated.vue +100 -92
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsSection.vue +55 -52
- package/lib/components/systems/scroll-anchors/_scroll-anchors-nav-animated.scss +66 -0
- package/lib/components/systems/scroll-anchors/useScrollAnchorSection.js +56 -0
- package/lib/components/systems/scroll-anchors/useScrollAnchorSections.js +15 -0
- package/lib/components/systems/scroll-anchors/useScrollAnchors.js +152 -0
- package/package.json +2 -2
- package/dist/types/components/systems/scroll-anchors/symbols.d.ts +0 -7
- package/dist/types/components/systems/scroll-anchors/symbols.d.ts.map +0 -1
- package/lib/components/systems/scroll-anchors/symbols.js +0 -6
|
@@ -1,153 +1,54 @@
|
|
|
1
|
-
<!-- Version: 0.0.2? (NEED to diff, unsure of changes) -->
|
|
2
1
|
<template>
|
|
3
|
-
<div class="scroll-anchors">
|
|
2
|
+
<div class="scroll-anchors" ref="componentEl">
|
|
4
3
|
<slot/>
|
|
5
4
|
</div>
|
|
6
5
|
</template>
|
|
7
6
|
|
|
8
|
-
<script>
|
|
9
|
-
import { computed } from "vue";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
emits: ["section-change"],
|
|
14
|
-
props: {
|
|
15
|
-
firstItemActive: Boolean,
|
|
16
|
-
/**
|
|
17
|
-
* Observe
|
|
18
|
-
*/
|
|
19
|
-
observerOptions: {
|
|
20
|
-
type: Object,
|
|
21
|
-
default: () => ({
|
|
22
|
-
root: null,
|
|
23
|
-
threshhold: [0,1],
|
|
24
|
-
rootMargin: "-25% 0px -55% 0px"
|
|
25
|
-
// root: null,
|
|
26
|
-
// threshhold: [0,1],
|
|
27
|
-
// rootMargin: "25% 0px 75% 0px"
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
data() {
|
|
32
|
-
return {
|
|
33
|
-
isMounted: false,
|
|
34
|
-
sections: [], // Child components will section themselves
|
|
35
|
-
};
|
|
36
|
-
},
|
|
7
|
+
<script setup>
|
|
8
|
+
import { ref, computed, provide } from "vue";
|
|
9
|
+
import { useScrollAnchors } from "./useScrollAnchors.js";
|
|
10
|
+
|
|
11
|
+
const props = defineProps({
|
|
37
12
|
/**
|
|
38
|
-
*
|
|
39
|
-
* - Uses symbols
|
|
13
|
+
* Make the first item active by default on load
|
|
40
14
|
*/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.update();
|
|
55
|
-
},
|
|
56
|
-
[UNREGISTER]: (instance) => {
|
|
57
|
-
const sections = this.sections;
|
|
58
|
-
const index = sections.findIndex(r => r.instance === instance);
|
|
59
|
-
if (index > -1) {
|
|
60
|
-
sections.splice(index, 1);
|
|
61
|
-
}
|
|
62
|
-
this.update();
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
},
|
|
66
|
-
methods: {
|
|
67
|
-
update() {
|
|
68
|
-
if (this.isMounted) {
|
|
69
|
-
this.observeItems();
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
getSectionIndex(el) {
|
|
73
|
-
return this.sections.findIndex(({ element }) => el === element);
|
|
74
|
-
},
|
|
75
|
-
/**
|
|
76
|
-
* Sets up a new observer to watch the section visibility
|
|
77
|
-
*/
|
|
78
|
-
createObserver() {
|
|
79
|
-
const { observerOptions, sections, removeActive, firstItemActive } = this;
|
|
80
|
-
let lastY = 0;
|
|
81
|
-
// Observer callback, basically just sets active state for a given slide
|
|
82
|
-
// - isIntersecting will change when the element enters and leaves
|
|
83
|
-
const onObserve = (entries) => {
|
|
84
|
-
entries.forEach(({ target, isIntersecting }) => {
|
|
85
|
-
const index = this.getSectionIndex(target);
|
|
86
|
-
const y = target.offsetTop;
|
|
87
|
-
const section = sections[index];
|
|
88
|
-
const firstExiting = index === 0 && lastY > y;
|
|
89
|
-
const lastExiting = index === sections.length - 1 && lastY < y;
|
|
90
|
-
if (section) {
|
|
91
|
-
this.$nextTick(() => {
|
|
92
|
-
if (isIntersecting) {
|
|
93
|
-
removeActive(section);
|
|
94
|
-
section.active = true;
|
|
95
|
-
// Only allow first and last to
|
|
96
|
-
} else if (firstExiting && !firstItemActive) {
|
|
97
|
-
removeActive();
|
|
98
|
-
} else if (lastExiting && section.active) {
|
|
99
|
-
removeActive();
|
|
100
|
-
}
|
|
101
|
-
this.$emit("section-change", {
|
|
102
|
-
section,
|
|
103
|
-
sections,
|
|
104
|
-
active: isIntersecting
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
};
|
|
110
|
-
// Add non-reactive prop for removal and changes to targets
|
|
111
|
-
this.observer = new IntersectionObserver(onObserve, observerOptions);
|
|
112
|
-
},
|
|
113
|
-
/**
|
|
114
|
-
* Add all slide elements as targets in observer
|
|
115
|
-
*/
|
|
116
|
-
observeItems() {
|
|
117
|
-
const { observer, sections } = this;
|
|
118
|
-
observer.disconnect();
|
|
119
|
-
sections.forEach(({ element }) => {
|
|
120
|
-
if (element) {
|
|
121
|
-
observer.observe(element);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
},
|
|
125
|
-
removeActive(except = null) {
|
|
126
|
-
this.sections.forEach(s => {
|
|
127
|
-
if (s !== except) {
|
|
128
|
-
s.active = false;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
/**
|
|
133
|
-
* Remove observer and it's internal DOM references (GC)
|
|
134
|
-
*/
|
|
135
|
-
destroyObserver() {
|
|
136
|
-
this.observer.disconnect();
|
|
137
|
-
this.observer = null;
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
mounted() {
|
|
141
|
-
const first = this.sections[0];
|
|
142
|
-
if (this.firstItemActive && first) {
|
|
143
|
-
first.active = true;
|
|
144
|
-
}
|
|
145
|
-
this.createObserver();
|
|
146
|
-
this.observeItems();
|
|
147
|
-
this.isMounted = true;
|
|
148
|
-
},
|
|
149
|
-
unmounted() {
|
|
150
|
-
this.destroyObserver();
|
|
15
|
+
firstItemActive: Boolean,
|
|
16
|
+
/**
|
|
17
|
+
* IntersectionObserver options
|
|
18
|
+
* - Defaults: { root: null, threshold: 0, rootMargin: "-25% 0px -55% 0px" }
|
|
19
|
+
* See: https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver
|
|
20
|
+
*/
|
|
21
|
+
observerOptions: {
|
|
22
|
+
type: Object,
|
|
23
|
+
default: () => ({
|
|
24
|
+
root: null,
|
|
25
|
+
threshold: 0,
|
|
26
|
+
rootMargin: "-25% 0px -55% 0px"
|
|
27
|
+
})
|
|
151
28
|
},
|
|
152
|
-
|
|
153
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Enable debug logging for the IntersectionObserver
|
|
31
|
+
*/
|
|
32
|
+
debug: Boolean
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const emit = defineEmits(["section-change"]);
|
|
36
|
+
|
|
37
|
+
const sections = ref([]);
|
|
38
|
+
const componentEl = ref(null);
|
|
39
|
+
|
|
40
|
+
useScrollAnchors({ sections, props, emit, componentElRef: componentEl });
|
|
41
|
+
|
|
42
|
+
provide('uluScrollAnchorsSections', computed(() => sections.value));
|
|
43
|
+
|
|
44
|
+
provide('uluScrollAnchorsRegister', (section) => {
|
|
45
|
+
sections.value.push(section);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
provide('uluScrollAnchorsUnregister', (sectionId) => {
|
|
49
|
+
const index = sections.value.findIndex(r => r.id === sectionId);
|
|
50
|
+
if (index > -1) {
|
|
51
|
+
sections.value.splice(index, 1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
</script>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
:is="element"
|
|
4
|
+
:class="['scroll-anchors__section', { 'is-active': isActive }]"
|
|
5
|
+
ref="sectionRef"
|
|
6
|
+
>
|
|
7
|
+
<slot :isActive="isActive" :titleId="titleId" :section="section" />
|
|
8
|
+
</component>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { useScrollAnchorSection } from "./useScrollAnchorSection";
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
/**
|
|
16
|
+
* The title of the section, used for navigation and generating a default ID
|
|
17
|
+
*/
|
|
18
|
+
title: {
|
|
19
|
+
type: String,
|
|
20
|
+
required: true
|
|
21
|
+
},
|
|
22
|
+
/**
|
|
23
|
+
* A custom ID to use for the section anchor, overriding the auto-generated one
|
|
24
|
+
*/
|
|
25
|
+
customTitleId: String,
|
|
26
|
+
/**
|
|
27
|
+
* Element to use
|
|
28
|
+
*/
|
|
29
|
+
element: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: "div"
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const { sectionRef, titleId, isActive, section } = useScrollAnchorSection(props);
|
|
36
|
+
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component
|
|
3
|
-
v-if="sections.length"
|
|
3
|
+
v-if="sections && sections.length"
|
|
4
4
|
:is="element"
|
|
5
5
|
class="scroll-anchors__nav"
|
|
6
6
|
>
|
|
@@ -13,25 +13,27 @@
|
|
|
13
13
|
:class="{ 'is-active' : item.active }"
|
|
14
14
|
:href="`#${ item.titleId }`"
|
|
15
15
|
>
|
|
16
|
-
|
|
16
|
+
<slot :item="item" :index="index">
|
|
17
|
+
{{ item.title }}
|
|
18
|
+
</slot>
|
|
17
19
|
</a>
|
|
18
20
|
</li>
|
|
19
21
|
</ul>
|
|
20
22
|
</component>
|
|
21
23
|
</template>
|
|
22
24
|
|
|
23
|
-
<script>
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
default: "nav"
|
|
34
|
-
}
|
|
25
|
+
<script setup>
|
|
26
|
+
import { useScrollAnchorSections } from "./useScrollAnchorSections.js";
|
|
27
|
+
|
|
28
|
+
defineProps({
|
|
29
|
+
/**
|
|
30
|
+
* The HTML element to use for the navigation root
|
|
31
|
+
*/
|
|
32
|
+
element: {
|
|
33
|
+
type: String,
|
|
34
|
+
default: "nav"
|
|
35
35
|
}
|
|
36
|
-
};
|
|
37
|
-
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const sections = useScrollAnchorSections();
|
|
39
|
+
</script>
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
Version: 0.0.2
|
|
3
|
-
Changes:
|
|
4
|
-
- 0.0.2 | Added transition initial state/class so the indicator
|
|
5
|
-
doesn't transition at first
|
|
6
|
-
-->
|
|
7
1
|
<template>
|
|
8
2
|
<component
|
|
9
|
-
v-if="sections.length"
|
|
3
|
+
v-if="sections && sections.length"
|
|
10
4
|
:is="element"
|
|
11
|
-
class="scroll-anchors__nav scroll-anchors__nav--animated"
|
|
5
|
+
class="scroll-anchors__nav scroll-anchors__nav--animated scroll-anchors-nav-animated"
|
|
6
|
+
:style="{ '--rail-width': `${railWidth}px` }"
|
|
12
7
|
>
|
|
13
|
-
<ul class="scroll-
|
|
8
|
+
<ul class="scroll-anchors-nav-animated__rail">
|
|
14
9
|
<li
|
|
15
10
|
v-for="(item, index) in sections" :key="index"
|
|
16
11
|
:class="{ 'is-active' : item.active }"
|
|
@@ -20,108 +15,121 @@ Changes:
|
|
|
20
15
|
:ref="(el) => addLinkRef(index, el)"
|
|
21
16
|
:href="`#${ item.titleId }`"
|
|
22
17
|
>
|
|
23
|
-
|
|
18
|
+
<slot :item="item" :index="index">
|
|
19
|
+
{{ item.title }}
|
|
20
|
+
</slot>
|
|
24
21
|
</a>
|
|
25
22
|
</li>
|
|
26
23
|
</ul>
|
|
27
24
|
<div
|
|
28
|
-
class="scroll-
|
|
25
|
+
class="scroll-anchors-nav-animated__indicator"
|
|
29
26
|
:class="{
|
|
30
|
-
'scroll-
|
|
27
|
+
'scroll-anchors-nav-animated__indicator--can-transition' : indicatorAnimReady
|
|
31
28
|
}"
|
|
32
29
|
ref="indicator"
|
|
33
|
-
:style="{
|
|
30
|
+
:style="{
|
|
34
31
|
opacity: indicatorStyles ? '1' : '0',
|
|
35
|
-
transform: `translateY(${ indicatorStyles.y }px)`,
|
|
36
|
-
height: `${ indicatorStyles.height }px`,
|
|
32
|
+
transform: `translateY(${ indicatorStyles ? indicatorStyles.y : 0 }px)`,
|
|
33
|
+
height: `${ indicatorStyles ? indicatorStyles.height : 0 }px`,
|
|
34
|
+
width: `${ indicatorStyles ? indicatorStyles.width : 0 }px`
|
|
37
35
|
}"
|
|
38
36
|
></div>
|
|
39
37
|
</component>
|
|
40
38
|
</template>
|
|
41
39
|
|
|
42
|
-
<script>
|
|
40
|
+
<script setup>
|
|
41
|
+
import { ref, computed, watch } from 'vue';
|
|
43
42
|
import { runAfterFramePaint } from "@ulu/utils/browser/performance.js";
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
import { useScrollAnchorSections } from './useScrollAnchorSections.js';
|
|
44
|
+
|
|
45
|
+
const props = defineProps({
|
|
46
|
+
/**
|
|
47
|
+
* The HTML element to use for the navigation root
|
|
48
|
+
*/
|
|
49
|
+
element: {
|
|
50
|
+
type: String,
|
|
51
|
+
default: "nav"
|
|
49
52
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
default: "nav"
|
|
57
|
-
},
|
|
53
|
+
/**
|
|
54
|
+
* The width of the navigation rail
|
|
55
|
+
*/
|
|
56
|
+
railWidth: {
|
|
57
|
+
type: Number,
|
|
58
|
+
default: 3
|
|
58
59
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
/**
|
|
61
|
+
* The width of the indicator, defaults to railWidth
|
|
62
|
+
*/
|
|
63
|
+
indicatorWidth: {
|
|
64
|
+
type: Number,
|
|
65
|
+
default: null
|
|
64
66
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (!sections || !linkCount) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
const activeIndex = sections.findIndex(s => s.active);
|
|
75
|
-
if (activeIndex === -1) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
const link = this.linkRefs[activeIndex];
|
|
79
|
-
const { offsetTop, offsetHeight } = link;
|
|
80
|
-
return {
|
|
81
|
-
y: offsetTop,
|
|
82
|
-
height: offsetHeight,
|
|
83
|
-
initial: this.inidica
|
|
84
|
-
};
|
|
85
|
-
}
|
|
67
|
+
/**
|
|
68
|
+
* If set, creates a static height, centered indicator
|
|
69
|
+
*/
|
|
70
|
+
indicatorHeight: {
|
|
71
|
+
type: Number,
|
|
72
|
+
default: null
|
|
86
73
|
},
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
}
|
|
74
|
+
/**
|
|
75
|
+
* Vertical alignment of the indicator relative to the link
|
|
76
|
+
*/
|
|
77
|
+
indicatorAlignment: {
|
|
78
|
+
type: String,
|
|
79
|
+
default: 'center' // options: center, top
|
|
95
80
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Pixel offset for the indicator's vertical alignment
|
|
83
|
+
*/
|
|
84
|
+
indicatorAlignmentOffset: {
|
|
85
|
+
type: Number,
|
|
86
|
+
default: 0
|
|
100
87
|
}
|
|
101
|
-
};
|
|
102
|
-
</script>
|
|
88
|
+
});
|
|
103
89
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
90
|
+
const sections = useScrollAnchorSections();
|
|
91
|
+
|
|
92
|
+
const linkRefs = ref({});
|
|
93
|
+
const indicatorAnimReady = ref(false);
|
|
94
|
+
const indicator = ref(null);
|
|
95
|
+
|
|
96
|
+
const indicatorStyles = computed(() => {
|
|
97
|
+
if (!sections || !sections.value || !sections.value.length) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const activeIndex = sections.value.findIndex(s => s.active);
|
|
101
|
+
if (activeIndex === -1) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const link = linkRefs.value[activeIndex];
|
|
105
|
+
if (!link) return false; // Link might not be rendered yet
|
|
106
|
+
|
|
107
|
+
const { offsetTop, offsetHeight } = link;
|
|
108
|
+
const isStatic = props.indicatorHeight != null;
|
|
109
|
+
const width = props.indicatorWidth ?? props.railWidth;
|
|
110
|
+
const height = isStatic ? props.indicatorHeight : offsetHeight;
|
|
111
|
+
|
|
112
|
+
let y = offsetTop; // Default to 'top' alignment
|
|
113
|
+
if (props.indicatorAlignment === 'center') {
|
|
114
|
+
y = offsetTop + (offsetHeight / 2) - (height / 2);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
y += props.indicatorAlignmentOffset;
|
|
118
|
+
|
|
119
|
+
return { y, height, width };
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
watch(indicatorStyles, (val) => {
|
|
123
|
+
if (val && !indicatorAnimReady.value) {
|
|
124
|
+
runAfterFramePaint(() => {
|
|
125
|
+
indicatorAnimReady.value = true;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
function addLinkRef(index, el) {
|
|
131
|
+
if (el) {
|
|
132
|
+
linkRefs.value[index] = el;
|
|
133
|
+
}
|
|
126
134
|
}
|
|
127
|
-
</
|
|
135
|
+
</script>
|
|
@@ -1,63 +1,66 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<component
|
|
3
|
+
:is="element"
|
|
4
|
+
:class="[wrapperClass, { [activeClass] : activeClass && isActive }]"
|
|
5
|
+
ref="sectionRef"
|
|
6
|
+
>
|
|
3
7
|
<component :is="titleElement" :class="titleClass" :id="titleId">
|
|
4
|
-
|
|
8
|
+
<slot name="title">
|
|
9
|
+
{{ title }}
|
|
10
|
+
</slot>
|
|
5
11
|
</component>
|
|
6
12
|
<slot :section="section"></slot>
|
|
7
|
-
</
|
|
13
|
+
</component>
|
|
8
14
|
</template>
|
|
9
15
|
|
|
10
|
-
<script>
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
default: "h2"
|
|
25
|
-
},
|
|
26
|
-
anchorId: String,
|
|
27
|
-
wrapperClass: {
|
|
28
|
-
type: String,
|
|
29
|
-
default: "scroll-anchors__section"
|
|
30
|
-
},
|
|
31
|
-
activeClass: {
|
|
32
|
-
type: String,
|
|
33
|
-
default: 'is-active'
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
inject: {
|
|
37
|
-
register: { from: REGISTER },
|
|
38
|
-
unregister: { from: UNREGISTER },
|
|
39
|
-
sections: { from: SECTIONS, default: () => computed(() => []) }
|
|
16
|
+
<script setup>
|
|
17
|
+
import { useScrollAnchorSection } from "./useScrollAnchorSection";
|
|
18
|
+
|
|
19
|
+
const props = defineProps({
|
|
20
|
+
/**
|
|
21
|
+
* The title of the section, used for navigation and generating a default ID
|
|
22
|
+
*/
|
|
23
|
+
title: String,
|
|
24
|
+
/**
|
|
25
|
+
* The HTML element to use for the title
|
|
26
|
+
*/
|
|
27
|
+
titleElement: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: "h2"
|
|
40
30
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The class to apply to the title element
|
|
33
|
+
*/
|
|
34
|
+
titleClass: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: "h2"
|
|
46
37
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
38
|
+
/**
|
|
39
|
+
* A custom ID to use for the section anchor, overriding the auto-generated one
|
|
40
|
+
*/
|
|
41
|
+
customTitleId: String,
|
|
42
|
+
/**
|
|
43
|
+
* The class to apply to the section's wrapper div
|
|
44
|
+
*/
|
|
45
|
+
wrapperClass: {
|
|
46
|
+
type: String,
|
|
47
|
+
default: "scroll-anchors__section"
|
|
51
48
|
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
/**
|
|
50
|
+
* The class to apply to the wrapper div when the section is active
|
|
51
|
+
*/
|
|
52
|
+
activeClass: {
|
|
53
|
+
type: String,
|
|
54
|
+
default: 'is-active'
|
|
56
55
|
},
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
/**
|
|
57
|
+
* The HTML element to use for the section root
|
|
58
|
+
*/
|
|
59
|
+
element: {
|
|
60
|
+
type: String,
|
|
61
|
+
default: 'section'
|
|
61
62
|
}
|
|
62
|
-
};
|
|
63
|
-
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const { sectionRef, titleId, isActive, section } = useScrollAnchorSection(props);
|
|
66
|
+
</script>
|