@ulu/frontend-vue 0.1.3-beta.2 → 0.1.3-beta.20
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 +3315 -2668
- package/dist/types/components/collapsible/UluAccordionGroup.vue.d.ts +20 -0
- package/dist/types/components/collapsible/UluAccordionGroup.vue.d.ts.map +1 -1
- package/dist/types/components/elements/UluList.vue.d.ts.map +1 -1
- package/dist/types/components/elements/UluRule.vue.d.ts +19 -0
- package/dist/types/components/elements/UluRule.vue.d.ts.map +1 -0
- package/dist/types/components/forms/UluFormRadio.vue.d.ts +4 -4
- package/dist/types/components/index.d.ts +12 -0
- 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/layout/UluWhenBreakpoint.vue.d.ts.map +1 -1
- package/dist/types/components/systems/facets/UluFacetsFilterSelects.vue.d.ts +21 -2
- package/dist/types/components/systems/facets/UluFacetsFilterSelects.vue.d.ts.map +1 -1
- package/dist/types/components/systems/facets/UluFacetsSearch.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 +27 -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/dist/types/plugins/popovers/defaults.d.ts.map +1 -1
- package/dist/types/plugins/popovers/index.d.ts.map +1 -1
- package/lib/components/_index.scss +1 -0
- package/lib/components/collapsible/UluAccordionGroup.vue +39 -5
- package/lib/components/collapsible/UluModal.vue +8 -8
- package/lib/components/elements/UluList.vue +3 -4
- package/lib/components/elements/UluRule.vue +49 -0
- package/lib/components/forms/UluFormRadio.vue +2 -2
- package/lib/components/index.js +12 -0
- package/lib/components/layout/UluDataGrid.vue +55 -15
- package/lib/components/layout/UluWhenBreakpoint.vue +14 -4
- package/lib/components/navigation/UluSkipLink.vue +1 -1
- package/lib/components/systems/facets/UluFacetsFilterSelects.vue +34 -7
- package/lib/components/systems/facets/UluFacetsSearch.vue +3 -3
- package/lib/components/systems/facets/UluFacetsSort.vue +3 -3
- 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 +50 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsNav.vue +18 -16
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsNavAnimated.vue +100 -89
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsSection.vue +65 -51
- package/lib/components/systems/scroll-anchors/_scroll-anchors-nav-animated.scss +67 -0
- package/lib/components/systems/scroll-anchors/useScrollAnchorSection.js +60 -0
- package/lib/components/systems/scroll-anchors/useScrollAnchorSections.js +15 -0
- package/lib/components/systems/scroll-anchors/useScrollAnchors.js +158 -0
- package/lib/plugins/popovers/defaults.js +10 -10
- package/lib/plugins/popovers/index.js +9 -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
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
:is="element"
|
|
4
|
+
:class="[
|
|
5
|
+
'scroll-anchors__section',
|
|
6
|
+
{ 'is-active': isActive }
|
|
7
|
+
]"
|
|
8
|
+
:data-scrollpoint-state="sectionState"
|
|
9
|
+
ref="sectionRef"
|
|
10
|
+
>
|
|
11
|
+
<slot :isActive="isActive" :titleId="titleId" :section="section" :inactiveFrom="inactiveFrom" :activeFrom="activeFrom" :sectionState="sectionState" />
|
|
12
|
+
</component>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup>
|
|
16
|
+
import { computed } from "vue";
|
|
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: {
|
|
24
|
+
type: String,
|
|
25
|
+
required: true
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* A custom ID to use for the section anchor, overriding the auto-generated one
|
|
29
|
+
*/
|
|
30
|
+
customTitleId: String,
|
|
31
|
+
/**
|
|
32
|
+
* Element to use
|
|
33
|
+
*/
|
|
34
|
+
element: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: "div"
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const { sectionRef, titleId, isActive, inactiveFrom, activeFrom, section } = useScrollAnchorSection(props);
|
|
41
|
+
|
|
42
|
+
const sectionState = computed(() => {
|
|
43
|
+
if (isActive.value) {
|
|
44
|
+
if (activeFrom.value) return `enter-${activeFrom.value}`;
|
|
45
|
+
} else {
|
|
46
|
+
if (inactiveFrom.value) return `exit-${inactiveFrom.value}`;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
});
|
|
50
|
+
</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="{ '--ulu-sa-nav-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,105 +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
|
-
|
|
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
|
+
}
|
|
123
134
|
}
|
|
124
|
-
</
|
|
135
|
+
</script>
|
|
@@ -1,63 +1,77 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<component
|
|
3
|
+
:is="element"
|
|
4
|
+
:class="[wrapperClass, { [activeClass]: activeClass && isActive }]"
|
|
5
|
+
:data-scrollpoint-state="sectionState"
|
|
6
|
+
ref="sectionRef"
|
|
7
|
+
>
|
|
3
8
|
<component :is="titleElement" :class="titleClass" :id="titleId">
|
|
4
|
-
|
|
9
|
+
<slot name="title">
|
|
10
|
+
{{ title }}
|
|
11
|
+
</slot>
|
|
5
12
|
</component>
|
|
6
13
|
<slot :section="section"></slot>
|
|
7
|
-
</
|
|
14
|
+
</component>
|
|
8
15
|
</template>
|
|
9
16
|
|
|
10
|
-
<script>
|
|
17
|
+
<script setup>
|
|
11
18
|
import { computed } from "vue";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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(() => []) }
|
|
19
|
+
import { useScrollAnchorSection } from "./useScrollAnchorSection";
|
|
20
|
+
|
|
21
|
+
const props = defineProps({
|
|
22
|
+
/**
|
|
23
|
+
* The title of the section, used for navigation and generating a default ID
|
|
24
|
+
*/
|
|
25
|
+
title: String,
|
|
26
|
+
/**
|
|
27
|
+
* The HTML element to use for the title
|
|
28
|
+
*/
|
|
29
|
+
titleElement: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: "h2"
|
|
40
32
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The class to apply to the title element
|
|
35
|
+
*/
|
|
36
|
+
titleClass: {
|
|
37
|
+
type: String,
|
|
38
|
+
default: "h2"
|
|
46
39
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
/**
|
|
41
|
+
* A custom ID to use for the section anchor, overriding the auto-generated one
|
|
42
|
+
*/
|
|
43
|
+
customTitleId: String,
|
|
44
|
+
/**
|
|
45
|
+
* The class to apply to the section's wrapper div
|
|
46
|
+
*/
|
|
47
|
+
wrapperClass: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: "scroll-anchors__section"
|
|
51
50
|
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
/**
|
|
52
|
+
* The class to apply to the wrapper div when the section is active
|
|
53
|
+
*/
|
|
54
|
+
activeClass: {
|
|
55
|
+
type: String,
|
|
56
|
+
default: 'is-active'
|
|
56
57
|
},
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The HTML element to use for the section root
|
|
60
|
+
*/
|
|
61
|
+
element: {
|
|
62
|
+
type: String,
|
|
63
|
+
default: 'section'
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const { sectionRef, titleId, isActive, inactiveFrom, activeFrom, section } = useScrollAnchorSection(props);
|
|
68
|
+
|
|
69
|
+
const sectionState = computed(() => {
|
|
70
|
+
if (isActive.value) {
|
|
71
|
+
if (activeFrom.value) return `enter-${activeFrom.value}`;
|
|
72
|
+
} else {
|
|
73
|
+
if (inactiveFrom.value) return `exit-${inactiveFrom.value}`;
|
|
61
74
|
}
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
return null;
|
|
76
|
+
});
|
|
77
|
+
</script>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// @group scroll-anchors-nav-animated
|
|
3
|
+
/// For use with UluScrollAnchorsNavAnimated vue component.
|
|
4
|
+
////
|
|
5
|
+
|
|
6
|
+
@use "sass:map";
|
|
7
|
+
@use "sass:meta";
|
|
8
|
+
|
|
9
|
+
@use "@ulu/frontend/scss/selector";
|
|
10
|
+
@use "@ulu/frontend/scss/utils";
|
|
11
|
+
@use "@ulu/frontend/scss/color";
|
|
12
|
+
|
|
13
|
+
// Used for function fallback
|
|
14
|
+
$-fallbacks: () !default;
|
|
15
|
+
|
|
16
|
+
/// Module Settings
|
|
17
|
+
/// @type Map
|
|
18
|
+
$config: (
|
|
19
|
+
"rail-border-color": #dcdcdc,
|
|
20
|
+
"rail-padding": 1rem,
|
|
21
|
+
"indicator-color": #000,
|
|
22
|
+
"indicator-clip-path": null,
|
|
23
|
+
"transition-duration": 250ms,
|
|
24
|
+
"transition-timing-function": ease-in-out
|
|
25
|
+
) !default;
|
|
26
|
+
|
|
27
|
+
/// Change modules $config
|
|
28
|
+
/// @param {Map} $changes Map of changes
|
|
29
|
+
@mixin set($changes) {
|
|
30
|
+
$config: map.merge($config, $changes) !global;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Get a config option
|
|
34
|
+
/// @param {Map} $name Name of property
|
|
35
|
+
@function get($name) {
|
|
36
|
+
$value: utils.require-map-get($config, $name, "scroll-anchors-nav-animated [config]");
|
|
37
|
+
@return utils.function-fallback($name, $value, $-fallbacks);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Prints component styles
|
|
41
|
+
/// @demo scroll-anchors-nav-animated
|
|
42
|
+
/// @example scss
|
|
43
|
+
/// @include ulu.component-scroll-anchors-nav-animated-styles();
|
|
44
|
+
|
|
45
|
+
@mixin styles {
|
|
46
|
+
$prefix: selector.class("scroll-anchors-nav-animated");
|
|
47
|
+
|
|
48
|
+
#{ $prefix } {
|
|
49
|
+
position: relative;
|
|
50
|
+
}
|
|
51
|
+
#{ $prefix }__rail {
|
|
52
|
+
border-left: var(--ulu-sa-nav-rail-width, 3px) solid color.get(get("rail-border-color"));
|
|
53
|
+
padding-left: get("rail-padding");
|
|
54
|
+
}
|
|
55
|
+
#{ $prefix }__indicator {
|
|
56
|
+
position: absolute;
|
|
57
|
+
top: 0;
|
|
58
|
+
left: 0;
|
|
59
|
+
background-color: color.get(get("indicator-color"));
|
|
60
|
+
clip-path: get("indicator-clip-path");
|
|
61
|
+
}
|
|
62
|
+
#{ $prefix }__indicator--can-transition {
|
|
63
|
+
transition-property: height, transform, width;
|
|
64
|
+
transition-timing-function: get("transition-timing-function");
|
|
65
|
+
transition-duration: get("transition-duration");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ref, onMounted, onUnmounted, inject, computed, reactive, watch } from 'vue';
|
|
2
|
+
import { urlize } from '@ulu/utils/string.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Composable for a component that acts as a section within the Scroll Anchors system.
|
|
6
|
+
* It handles registering the section with the parent `UluScrollAnchors` component,
|
|
7
|
+
* manages its active state, and provides reactive properties for use in the template.
|
|
8
|
+
* @param {object} props The component props, requires `title` and optional `customTitleId`.
|
|
9
|
+
* @returns {object} An object with reactive properties for the section: `sectionRef` (the ref to bind), `titleId`, `isActive`, and the raw `section` object.
|
|
10
|
+
*/
|
|
11
|
+
export function useScrollAnchorSection(props) {
|
|
12
|
+
const sectionRef = ref(null); // for the user to bind to their root element
|
|
13
|
+
|
|
14
|
+
const register = inject('uluScrollAnchorsRegister');
|
|
15
|
+
const unregister = inject('uluScrollAnchorsUnregister');
|
|
16
|
+
const newId = title => `ulu-sa-${ urlize(title) }`;
|
|
17
|
+
|
|
18
|
+
const titleId = computed(() => props.customTitleId || newId(props.title));
|
|
19
|
+
|
|
20
|
+
const section = reactive({
|
|
21
|
+
id: Symbol('section-id'),
|
|
22
|
+
title: props.title,
|
|
23
|
+
titleId: titleId.value,
|
|
24
|
+
element: null, // will be set on mount
|
|
25
|
+
active: false,
|
|
26
|
+
inactiveFrom: null,
|
|
27
|
+
activeFrom: null
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Keep title and titleId in sync with props
|
|
31
|
+
watch(() => props.title, (newTitle) => {
|
|
32
|
+
section.title = newTitle;
|
|
33
|
+
section.titleId = props.customTitleId || newId(newTitle);
|
|
34
|
+
});
|
|
35
|
+
watch(() => props.customTitleId, (newTitleId) => {
|
|
36
|
+
section.titleId = newTitleId || newId(props.title);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
onMounted(() => {
|
|
40
|
+
if (register && sectionRef.value) {
|
|
41
|
+
section.element = sectionRef.value;
|
|
42
|
+
register(section);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
onUnmounted(() => {
|
|
47
|
+
if (unregister) {
|
|
48
|
+
unregister(section.id);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
sectionRef, // the ref for the user to attach
|
|
54
|
+
titleId,
|
|
55
|
+
isActive: computed(() => section.active),
|
|
56
|
+
inactiveFrom: computed(() => section.inactiveFrom),
|
|
57
|
+
activeFrom: computed(() => section.activeFrom),
|
|
58
|
+
section
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { inject } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Composable that provides a reactive list of all registered scroll anchor sections.
|
|
5
|
+
* This is the recommended way to access section data for building custom navigation.
|
|
6
|
+
* Must be used within a component that is a descendant of `UluScrollAnchors`.
|
|
7
|
+
* @returns {object} A reactive ref containing an array of section objects.
|
|
8
|
+
*/
|
|
9
|
+
export function useScrollAnchorSections() {
|
|
10
|
+
const sections = inject('uluScrollAnchorsSections');
|
|
11
|
+
if (!sections) {
|
|
12
|
+
console.warn('useScrollAnchorSections() must be used within an UluScrollAnchors component provider.');
|
|
13
|
+
}
|
|
14
|
+
return sections;
|
|
15
|
+
}
|