@ulu/frontend-vue 0.1.0-beta.1
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/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/breakpoints-ClT9bfZm.js +211 -0
- package/dist/frontend-vue.css +1 -0
- package/dist/frontend-vue.js +82 -0
- package/dist/frontend-vue.umd.cjs +561 -0
- package/dist/index-P5Rwl_Dl.js +7263 -0
- package/dist/index.es-HlG3u0J5.js +3134 -0
- package/lib/_index.scss +14 -0
- package/lib/components/_index.scss +6 -0
- package/lib/components/collapsible/UluAccordion.vue +82 -0
- package/lib/components/collapsible/UluCollapsibleRegion.vue +278 -0
- package/lib/components/collapsible/UluDropdown.vue +42 -0
- package/lib/components/collapsible/UluModal.vue +384 -0
- package/lib/components/collapsible/UluOverflowPopover.vue +52 -0
- package/lib/components/collapsible/UluTab.vue +9 -0
- package/lib/components/collapsible/UluTabGroup.vue +31 -0
- package/lib/components/collapsible/UluTabList.vue +9 -0
- package/lib/components/collapsible/UluTabPanel.vue +9 -0
- package/lib/components/collapsible/UluTabPanels.vue +9 -0
- package/lib/components/elements/UluAlert.vue +81 -0
- package/lib/components/elements/UluBadge.vue +58 -0
- package/lib/components/elements/UluBadgeStack.vue +27 -0
- package/lib/components/elements/UluButton.vue +161 -0
- package/lib/components/elements/UluCallout.vue +30 -0
- package/lib/components/elements/UluCard.vue +241 -0
- package/lib/components/elements/UluDefinitionList.vue +40 -0
- package/lib/components/elements/UluExternalLink.vue +47 -0
- package/lib/components/elements/UluIcon.vue +108 -0
- package/lib/components/elements/UluList.vue +87 -0
- package/lib/components/elements/UluMain.vue +5 -0
- package/lib/components/elements/UluSpokeSpinner.vue +25 -0
- package/lib/components/elements/UluTag.vue +53 -0
- package/lib/components/forms/UluCheckboxMenu.vue +36 -0
- package/lib/components/forms/UluFileDisplay.vue +39 -0
- package/lib/components/forms/UluFormDropzone.vue +62 -0
- package/lib/components/forms/UluFormFile.vue +47 -0
- package/lib/components/forms/UluFormMessage.vue +20 -0
- package/lib/components/forms/UluFormSelect.vue +37 -0
- package/lib/components/forms/UluFormText.vue +32 -0
- package/lib/components/forms/UluSearchForm.vue +31 -0
- package/lib/components/index.js +54 -0
- package/lib/components/layout/UluAdaptiveLayout.vue +11 -0
- package/lib/components/layout/UluDataGrid.vue +41 -0
- package/lib/components/layout/UluTitleRail.vue +56 -0
- package/lib/components/layout/UluWhenBreakpoint.vue +86 -0
- package/lib/components/navigation/UluBreadcrumb.vue +72 -0
- package/lib/components/navigation/UluMenu.vue +105 -0
- package/lib/components/navigation/UluMenuStack.vue +49 -0
- package/lib/components/navigation/UluNavStrip.vue +48 -0
- package/lib/components/navigation/UluSkipLink.vue +5 -0
- package/lib/components/systems/facets/UluFacets.vue +380 -0
- package/lib/components/systems/facets/UluFacetsList.vue +39 -0
- package/lib/components/systems/facets/UluFacetsSearch.vue +67 -0
- package/lib/components/systems/facets/_facets.scss +64 -0
- package/lib/components/systems/index.js +17 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchors.vue +152 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsNav.vue +37 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsNavAnimated.vue +124 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsSection.vue +63 -0
- package/lib/components/systems/scroll-anchors/symbols.js +6 -0
- package/lib/components/systems/skeleton/UluShowSkeleton.vue +13 -0
- package/lib/components/systems/skeleton/UluSkeletonContent.vue +60 -0
- package/lib/components/systems/skeleton/UluSkeletonMedia.vue +11 -0
- package/lib/components/systems/skeleton/UluSkeletonTextInline.vue +9 -0
- package/lib/components/systems/slider/UluImageSlideShow.vue +75 -0
- package/lib/components/systems/slider/UluSlideShow.vue +331 -0
- package/lib/components/systems/slider/UluSlideShowSlide.vue +25 -0
- package/lib/components/systems/table-sticky/UluTableSticky.vue +793 -0
- package/lib/components/systems/table-sticky/UluTableStickyRows.vue +73 -0
- package/lib/components/systems/table-sticky/UluTableStickyTable.vue +237 -0
- package/lib/components/systems/table-sticky/_table-sticky.scss +185 -0
- package/lib/components/utils/UluCondText.vue +28 -0
- package/lib/components/utils/UluEmpty.vue +3 -0
- package/lib/components/utils/UluEmptyView.vue +3 -0
- package/lib/components/utils/UluPlaceholderImage.vue +53 -0
- package/lib/components/utils/UluPlaceholderText.vue +25 -0
- package/lib/components/utils/UluRouteAnnouncer.vue +83 -0
- package/lib/components/visualizations/UluAnimateNumber.vue +32 -0
- package/lib/components/visualizations/UluProgressBar.vue +94 -0
- package/lib/components/visualizations/UluProgressDonut.vue +97 -0
- package/lib/composables/index.js +10 -0
- package/lib/composables/useBreakpointManager.js +68 -0
- package/lib/composables/useIcon.js +62 -0
- package/lib/composables/useModifiers.js +93 -0
- package/lib/composables/useWindowResize.js +64 -0
- package/lib/index.js +10 -0
- package/lib/plugins/_index.scss +7 -0
- package/lib/plugins/breakpoints/index.js +47 -0
- package/lib/plugins/index.js +11 -0
- package/lib/plugins/modals/UluModalsDisplay.vue +59 -0
- package/lib/plugins/modals/api.js +76 -0
- package/lib/plugins/modals/index.js +60 -0
- package/lib/plugins/modals/useModals.js +9 -0
- package/lib/plugins/popovers/UluPopover.vue +189 -0
- package/lib/plugins/popovers/UluTooltipDisplay.vue +15 -0
- package/lib/plugins/popovers/UluTooltipPopover.vue +83 -0
- package/lib/plugins/popovers/defaults.js +108 -0
- package/lib/plugins/popovers/directive.js +95 -0
- package/lib/plugins/popovers/index.js +18 -0
- package/lib/plugins/popovers/manager.js +54 -0
- package/lib/plugins/popovers/useFollow.js +80 -0
- package/lib/plugins/popovers/utils.js +5 -0
- package/lib/plugins/toast/UluToast.vue +87 -0
- package/lib/plugins/toast/UluToastDisplay.vue +35 -0
- package/lib/plugins/toast/_toast.scss +198 -0
- package/lib/plugins/toast/defaults.js +30 -0
- package/lib/plugins/toast/index.js +17 -0
- package/lib/plugins/toast/store.js +71 -0
- package/lib/plugins/toast/useToast.js +18 -0
- package/lib/settings.js +119 -0
- package/lib/utils/dom.js +14 -0
- package/lib/utils/placeholder.js +6 -0
- package/lib/utils/vue-router.js +219 -0
- package/package.json +75 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<!-- Version: 0.0.2? (NEED to diff, unsure of changes) -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="scroll-anchors">
|
|
4
|
+
<slot/>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import { computed } from "vue";
|
|
10
|
+
import { SECTIONS, REGISTER, UNREGISTER } from "./symbols.js";
|
|
11
|
+
export default {
|
|
12
|
+
name: "ScrollAnchors",
|
|
13
|
+
props: {
|
|
14
|
+
firstItemActive: Boolean,
|
|
15
|
+
/**
|
|
16
|
+
* Observe
|
|
17
|
+
*/
|
|
18
|
+
observerOptions: {
|
|
19
|
+
type: Object,
|
|
20
|
+
default: () => ({
|
|
21
|
+
root: null,
|
|
22
|
+
threshhold: [0,1],
|
|
23
|
+
rootMargin: "-25% 0px -55% 0px"
|
|
24
|
+
// root: null,
|
|
25
|
+
// threshhold: [0,1],
|
|
26
|
+
// rootMargin: "25% 0px 75% 0px"
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
data() {
|
|
31
|
+
return {
|
|
32
|
+
isMounted: false,
|
|
33
|
+
sections: [], // Child components will section themselves
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* Interface for chil components to register themselves
|
|
38
|
+
* - Uses symbols
|
|
39
|
+
*/
|
|
40
|
+
provide() {
|
|
41
|
+
return {
|
|
42
|
+
[SECTIONS]: computed(() => this.sections),
|
|
43
|
+
[REGISTER]: (instance) => {
|
|
44
|
+
const { titleId, title } = instance;
|
|
45
|
+
const { element } = instance.$refs;
|
|
46
|
+
this.sections.push({
|
|
47
|
+
instance,
|
|
48
|
+
titleId,
|
|
49
|
+
title,
|
|
50
|
+
element,
|
|
51
|
+
active: false
|
|
52
|
+
});
|
|
53
|
+
this.update();
|
|
54
|
+
},
|
|
55
|
+
[UNREGISTER]: (instance) => {
|
|
56
|
+
const sections = this.sections;
|
|
57
|
+
const index = sections.findIndex(r => r.instance === instance);
|
|
58
|
+
if (index > -1) {
|
|
59
|
+
sections.splice(index, 1);
|
|
60
|
+
}
|
|
61
|
+
this.update();
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
methods: {
|
|
66
|
+
update() {
|
|
67
|
+
if (this.isMounted) {
|
|
68
|
+
this.observeItems();
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
getSectionIndex(el) {
|
|
72
|
+
return this.sections.findIndex(({ element }) => el === element);
|
|
73
|
+
},
|
|
74
|
+
/**
|
|
75
|
+
* Sets up a new observer to watch the section visibility
|
|
76
|
+
*/
|
|
77
|
+
createObserver() {
|
|
78
|
+
const { observerOptions, sections, removeActive, firstItemActive } = this;
|
|
79
|
+
let lastY = 0;
|
|
80
|
+
// Observer callback, basically just sets active state for a given slide
|
|
81
|
+
// - isIntersecting will change when the element enters and leaves
|
|
82
|
+
const onObserve = (entries) => {
|
|
83
|
+
entries.forEach(({ target, isIntersecting }) => {
|
|
84
|
+
const index = this.getSectionIndex(target);
|
|
85
|
+
const y = target.offsetTop;
|
|
86
|
+
const section = sections[index];
|
|
87
|
+
const firstExiting = index === 0 && lastY > y;
|
|
88
|
+
const lastExiting = index === sections.length - 1 && lastY < y;
|
|
89
|
+
if (section) {
|
|
90
|
+
this.$nextTick(() => {
|
|
91
|
+
if (isIntersecting) {
|
|
92
|
+
removeActive(section);
|
|
93
|
+
section.active = true;
|
|
94
|
+
// Only allow first and last to
|
|
95
|
+
} else if (firstExiting && !firstItemActive) {
|
|
96
|
+
removeActive();
|
|
97
|
+
} else if (lastExiting && section.active) {
|
|
98
|
+
removeActive();
|
|
99
|
+
}
|
|
100
|
+
this.$emit("sectionChange", {
|
|
101
|
+
section,
|
|
102
|
+
sections,
|
|
103
|
+
active: isIntersecting
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
// Add non-reactive prop for removal and changes to targets
|
|
110
|
+
this.observer = new IntersectionObserver(onObserve, observerOptions);
|
|
111
|
+
},
|
|
112
|
+
/**
|
|
113
|
+
* Add all slide elements as targets in observer
|
|
114
|
+
*/
|
|
115
|
+
observeItems() {
|
|
116
|
+
const { observer, sections } = this;
|
|
117
|
+
observer.disconnect();
|
|
118
|
+
sections.forEach(({ element }) => {
|
|
119
|
+
if (element) {
|
|
120
|
+
observer.observe(element);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
removeActive(except = null) {
|
|
125
|
+
this.sections.forEach(s => {
|
|
126
|
+
if (s !== except) {
|
|
127
|
+
s.active = false;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
/**
|
|
132
|
+
* Remove observer and it's internal DOM references (GC)
|
|
133
|
+
*/
|
|
134
|
+
destroyObserver() {
|
|
135
|
+
this.observer.disconnect();
|
|
136
|
+
this.observer = null;
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
mounted() {
|
|
140
|
+
const first = this.sections[0];
|
|
141
|
+
if (this.firstItemActive && first) {
|
|
142
|
+
first.active = true;
|
|
143
|
+
}
|
|
144
|
+
this.createObserver();
|
|
145
|
+
this.observeItems();
|
|
146
|
+
this.isMounted = true;
|
|
147
|
+
},
|
|
148
|
+
unmounted() {
|
|
149
|
+
this.destroyObserver();
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
</script>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
v-if="sections.length"
|
|
4
|
+
:is="element"
|
|
5
|
+
class="scroll-anchors__nav"
|
|
6
|
+
>
|
|
7
|
+
<ul>
|
|
8
|
+
<li
|
|
9
|
+
v-for="(item, index) in sections" :key="index"
|
|
10
|
+
:class="{ 'is-active' : item.active }"
|
|
11
|
+
>
|
|
12
|
+
<a
|
|
13
|
+
:class="{ 'is-active' : item.active }"
|
|
14
|
+
:href="`#${ item.titleId }`"
|
|
15
|
+
>
|
|
16
|
+
{{ item.title }}
|
|
17
|
+
</a>
|
|
18
|
+
</li>
|
|
19
|
+
</ul>
|
|
20
|
+
</component>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script>
|
|
24
|
+
import { SECTIONS } from "./symbols.js";
|
|
25
|
+
export default {
|
|
26
|
+
name: "ScrollAnchorsNav",
|
|
27
|
+
inject: {
|
|
28
|
+
sections: { from: SECTIONS }
|
|
29
|
+
},
|
|
30
|
+
props: {
|
|
31
|
+
element: {
|
|
32
|
+
type: String,
|
|
33
|
+
default: "nav"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
</script>
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
<template>
|
|
8
|
+
<component
|
|
9
|
+
v-if="sections.length"
|
|
10
|
+
:is="element"
|
|
11
|
+
class="scroll-anchors__nav scroll-anchors__nav--animated"
|
|
12
|
+
>
|
|
13
|
+
<ul class="scroll-anchors__rail">
|
|
14
|
+
<li
|
|
15
|
+
v-for="(item, index) in sections" :key="index"
|
|
16
|
+
:class="{ 'is-active' : item.active }"
|
|
17
|
+
>
|
|
18
|
+
<a
|
|
19
|
+
:class="{ 'is-active' : item.active }"
|
|
20
|
+
:ref="(el) => addLinkRef(index, el)"
|
|
21
|
+
:href="`#${ item.titleId }`"
|
|
22
|
+
>
|
|
23
|
+
{{ item.title }}
|
|
24
|
+
</a>
|
|
25
|
+
</li>
|
|
26
|
+
</ul>
|
|
27
|
+
<div
|
|
28
|
+
class="scroll-anchors__indicator"
|
|
29
|
+
:class="{
|
|
30
|
+
'scroll-anchors__indicator--can-transition' : indicatorAnimReady
|
|
31
|
+
}"
|
|
32
|
+
ref="indicator"
|
|
33
|
+
:style="{
|
|
34
|
+
opacity: indicatorStyles ? '1' : '0',
|
|
35
|
+
transform: `translateY(${ indicatorStyles.y }px)`,
|
|
36
|
+
height: `${ indicatorStyles.height }px`,
|
|
37
|
+
}"
|
|
38
|
+
></div>
|
|
39
|
+
</component>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<script>
|
|
43
|
+
import { runAfterFramePaint } from "@ulu/utils/browser/performance.js";
|
|
44
|
+
import { SECTIONS } from "./symbols.js";
|
|
45
|
+
export default {
|
|
46
|
+
name: "ScrollAnchorsNavAnimated",
|
|
47
|
+
inject: {
|
|
48
|
+
sections: { from: SECTIONS }
|
|
49
|
+
},
|
|
50
|
+
props: {
|
|
51
|
+
/**
|
|
52
|
+
* Element to use for container
|
|
53
|
+
*/
|
|
54
|
+
element: {
|
|
55
|
+
type: String,
|
|
56
|
+
default: "nav"
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
data() {
|
|
60
|
+
return {
|
|
61
|
+
linkRefs: {},
|
|
62
|
+
indicatorAnimReady: false
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
computed: {
|
|
66
|
+
indicatorStyles() {
|
|
67
|
+
const { sections, linkRefs } = this;
|
|
68
|
+
const linkCount = Object.keys(linkRefs).length;
|
|
69
|
+
// Checking for sections and link refs incase
|
|
70
|
+
// we were waiting for the components to mount, etc
|
|
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
|
+
}
|
|
86
|
+
},
|
|
87
|
+
watch: {
|
|
88
|
+
indicatorStyles(val) {
|
|
89
|
+
if (val && !this.indicatorAnimReady) {
|
|
90
|
+
runAfterFramePaint(() => {
|
|
91
|
+
this.indicatorAnimReady = true;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
methods: {
|
|
97
|
+
addLinkRef(index, el) {
|
|
98
|
+
this.linkRefs[index] = el;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<style lang="scss">
|
|
105
|
+
// User can override these styles
|
|
106
|
+
// - Think this is better than props/etc
|
|
107
|
+
// - Refactored from props to just plain css to be overridden
|
|
108
|
+
.scroll-anchors__rail {
|
|
109
|
+
border-left: 3px solid rgb(220, 220, 220);
|
|
110
|
+
padding-left: 1rem;
|
|
111
|
+
}
|
|
112
|
+
.scroll-anchors__indicator {
|
|
113
|
+
position: absolute;
|
|
114
|
+
top: 0;
|
|
115
|
+
left: 0;
|
|
116
|
+
width: 3px;
|
|
117
|
+
background-color: black;
|
|
118
|
+
}
|
|
119
|
+
.scroll-anchors__indicator--can-transition {
|
|
120
|
+
transition-property: height, transform;
|
|
121
|
+
transition-timing-function: ease-in-out;
|
|
122
|
+
transition-duration: 250ms;
|
|
123
|
+
}
|
|
124
|
+
</style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="[wrapperClass, { [activeClass] : activeClass && section?.active }]" ref="element">
|
|
3
|
+
<component :is="titleElement" :class="titleClass" :id="titleId">
|
|
4
|
+
{{ title }}
|
|
5
|
+
</component>
|
|
6
|
+
<slot :section="section"></slot>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script>
|
|
11
|
+
import { computed } from "vue";
|
|
12
|
+
import { urlize } from "@ulu/utils/string.js";
|
|
13
|
+
import { REGISTER, UNREGISTER, SECTIONS } from "./symbols.js";
|
|
14
|
+
export default {
|
|
15
|
+
name: "ScrollAnchorsSection",
|
|
16
|
+
props: {
|
|
17
|
+
title: String,
|
|
18
|
+
titleElement: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: "h2"
|
|
21
|
+
},
|
|
22
|
+
titleClass: {
|
|
23
|
+
type: String,
|
|
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(() => []) }
|
|
40
|
+
},
|
|
41
|
+
data() {
|
|
42
|
+
const { anchorId, title } = this;
|
|
43
|
+
return {
|
|
44
|
+
titleId: anchorId || `sas-title-${ urlize(title) }`
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
computed: {
|
|
48
|
+
section() {
|
|
49
|
+
return this.sections.find(s => s.instance === this);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
mounted() {
|
|
53
|
+
if (this.register) {
|
|
54
|
+
this.register(this);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
unmounted() {
|
|
58
|
+
if (this.unregister) {
|
|
59
|
+
this.unregister(this);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
</script>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="skeleton">
|
|
3
|
+
<div v-for="(line, index) in linesWithSegments" :key="index">
|
|
4
|
+
<span
|
|
5
|
+
v-for="segment in line"
|
|
6
|
+
:key="segment"
|
|
7
|
+
class="skeleton__text skeleton__text--inline"
|
|
8
|
+
:class="{ 'skeleton__alt' : segment.alt }"
|
|
9
|
+
:style="{ width: `${ segment.width }%` }"
|
|
10
|
+
>
|
|
11
|
+
</span>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script>
|
|
17
|
+
import { randomInt } from "@ulu/utils/random.js";
|
|
18
|
+
import { arrayCreate } from "@ulu/utils/array.js";
|
|
19
|
+
export default {
|
|
20
|
+
name: "SkeletonContent",
|
|
21
|
+
props: {
|
|
22
|
+
lines: {
|
|
23
|
+
type: Number,
|
|
24
|
+
default: 6
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
methods: {
|
|
28
|
+
randomInt
|
|
29
|
+
},
|
|
30
|
+
computed: {
|
|
31
|
+
/**
|
|
32
|
+
* Creates the segments (like words) for the given line count
|
|
33
|
+
* - Uses random number of segments and makes sure to fill the line between 70% - 100%
|
|
34
|
+
*/
|
|
35
|
+
linesWithSegments() {
|
|
36
|
+
return arrayCreate(this.lines, () => {
|
|
37
|
+
const minWidth = 15;
|
|
38
|
+
const total = randomInt(70, 100);
|
|
39
|
+
let widthCurrent = 0;
|
|
40
|
+
const newWidth = () => {
|
|
41
|
+
const remaining = total - widthCurrent;
|
|
42
|
+
const width = randomInt(minWidth, remaining);
|
|
43
|
+
widthCurrent += width;
|
|
44
|
+
return width;
|
|
45
|
+
};
|
|
46
|
+
const segments = [];
|
|
47
|
+
while (widthCurrent < total - minWidth) {
|
|
48
|
+
segments.push(newWidth());
|
|
49
|
+
}
|
|
50
|
+
const getActualTotal = () => segments.reduce((acc, a) => acc + a, 0);
|
|
51
|
+
while (getActualTotal() >= total) {
|
|
52
|
+
let removed = segments.pop();
|
|
53
|
+
if (!removed) break;
|
|
54
|
+
}
|
|
55
|
+
return segments.map(width => ({ width, alt: Math.random() < 0.5 }));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
</script>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Note: This is the image implementation of SlideShow
|
|
3
|
+
Notes:
|
|
4
|
+
- Should the images resize to fit the nav?
|
|
5
|
+
- Should the nav slide based on the active index?
|
|
6
|
+
|
|
7
|
+
-->
|
|
8
|
+
<template>
|
|
9
|
+
<UluSlideShow
|
|
10
|
+
class="slideshow--images"
|
|
11
|
+
:items="images"
|
|
12
|
+
@slideChange="slideChange"
|
|
13
|
+
>
|
|
14
|
+
<template #slide="{ item }">
|
|
15
|
+
<img :src="item.src" :alt="item.alt">
|
|
16
|
+
<!-- Adding this in here for now, not generic meant for selection in compare ui -->
|
|
17
|
+
<div class="slideshow__image-actions">
|
|
18
|
+
<AppButton v-if="selectButton" class="type-small" icon="plus" small iconBefore>
|
|
19
|
+
Select
|
|
20
|
+
</AppButton>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
<template #nav="{ index }">
|
|
24
|
+
<img :src="images[index].src" :alt="`View image ${ index }`">
|
|
25
|
+
</template>
|
|
26
|
+
</UluSlideShow>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script>
|
|
30
|
+
import UluSlideShow from "./UluSlideShow.vue";
|
|
31
|
+
export default {
|
|
32
|
+
name: 'ImageSlideShow',
|
|
33
|
+
components: {
|
|
34
|
+
UluSlideShow,
|
|
35
|
+
},
|
|
36
|
+
props: {
|
|
37
|
+
images: Array,
|
|
38
|
+
selectButton: Boolean
|
|
39
|
+
},
|
|
40
|
+
watch: {
|
|
41
|
+
images() {
|
|
42
|
+
console.log('watch image from outer');
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
methods: {
|
|
46
|
+
/**
|
|
47
|
+
* Test to see if the active slide's nav button (thumbnail) is in view
|
|
48
|
+
* if not, scroll the nav to ensure it is visible. This function will take
|
|
49
|
+
* into account which side of the overflow nav the item is hidden in and will
|
|
50
|
+
* scroll either to fit it to the flush end (if overflow to the right) or flush start
|
|
51
|
+
*/
|
|
52
|
+
slideChange({ slide, nav }) {
|
|
53
|
+
const { active, navElement } = slide;
|
|
54
|
+
if (!navElement) return;
|
|
55
|
+
const { offsetWidth, scrollLeft: left } = nav;
|
|
56
|
+
const { offsetLeft, offsetWidth: navWidth } = navElement;
|
|
57
|
+
const right = left + offsetWidth;
|
|
58
|
+
const navRight = offsetLeft + navWidth;
|
|
59
|
+
let amount = null;
|
|
60
|
+
console.log('left/right', left, right);
|
|
61
|
+
if (active && navElement) {
|
|
62
|
+
|
|
63
|
+
if (navRight > right) {
|
|
64
|
+
amount = left + (navRight - right);
|
|
65
|
+
} else if (offsetLeft < left) {
|
|
66
|
+
amount = offsetLeft;
|
|
67
|
+
}
|
|
68
|
+
if (amount !== null) {
|
|
69
|
+
nav.scrollTo({ left: amount, top: 0, behavior: "smooth" });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</script>
|