@energysage/es-ds-components 3.0.0-alpha.7 → 3.0.0-alpha.8
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/components/es-col.vue +4 -2
- package/components/es-skeleton-img.vue +38 -0
- package/components/es-skeleton-wrapper.vue +17 -0
- package/components/es-skeleton.vue +97 -0
- package/components/es-slider.vue +114 -43
- package/components/es-tabs.vue +1 -1
- package/components/es-video.vue +58 -0
- package/nuxt.config.ts +1 -0
- package/package.json +1 -1
- package/utils/get-es-nav-bar-global-content.js +8 -0
package/components/es-col.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
defineProps<{
|
|
2
|
+
const props = defineProps<{
|
|
3
3
|
cols?: string;
|
|
4
4
|
sm?: string;
|
|
5
5
|
md?: string;
|
|
@@ -7,12 +7,14 @@ defineProps<{
|
|
|
7
7
|
xl?: string;
|
|
8
8
|
xxl?: string;
|
|
9
9
|
}>();
|
|
10
|
+
|
|
11
|
+
const undefinedSize = !(props.cols || props.sm || props.md || props.lg || props.xl || props.xxl);
|
|
10
12
|
</script>
|
|
11
13
|
|
|
12
14
|
<template>
|
|
13
15
|
<div
|
|
14
|
-
class="col"
|
|
15
16
|
:class="{
|
|
17
|
+
col: undefinedSize,
|
|
16
18
|
[`col-${cols}`]: cols,
|
|
17
19
|
[`col-sm-${sm}`]: sm,
|
|
18
20
|
[`col-md-${md}`]: md,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
aspect: {
|
|
4
|
+
type: String,
|
|
5
|
+
default: '16:9',
|
|
6
|
+
},
|
|
7
|
+
noAspect: {
|
|
8
|
+
type: Boolean,
|
|
9
|
+
default: false,
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const aspects = props.aspect.split(':');
|
|
14
|
+
const aspectRatio = computed(() => `${(parseFloat(aspects[1]) / parseFloat(aspects[0])) * 100}%`);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<!-- if noAspect is set, pass along to esSkeleton with all other props -->
|
|
19
|
+
<es-skeleton
|
|
20
|
+
v-if="noAspect"
|
|
21
|
+
v-bind="$attrs" />
|
|
22
|
+
<!-- if setting the aspect ratio, wrap skeleton in containers to determine responsive size -->
|
|
23
|
+
<!-- based on Bootstrap Aspect -->
|
|
24
|
+
<div
|
|
25
|
+
v-else
|
|
26
|
+
class="d-flex">
|
|
27
|
+
<div
|
|
28
|
+
class="flex-grow-1"
|
|
29
|
+
:style="{ 'padding-bottom': aspectRatio, height: '0px' }" />
|
|
30
|
+
<div
|
|
31
|
+
class="flex-grow-1 w-100 mw-100"
|
|
32
|
+
style="margin-left: -100%">
|
|
33
|
+
<es-skeleton
|
|
34
|
+
v-bind="$attrs"
|
|
35
|
+
class="h-100 w-100" />
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import Skeleton from 'primevue/skeleton';
|
|
3
|
+
|
|
4
|
+
defineProps({
|
|
5
|
+
animation: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: 'wave',
|
|
8
|
+
validator: (val: string) => ['wave', 'fade', 'none'].includes(val as string),
|
|
9
|
+
},
|
|
10
|
+
height: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: '1rem',
|
|
13
|
+
},
|
|
14
|
+
width: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: 'auto',
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: null,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<skeleton
|
|
27
|
+
:height="size ? size : height"
|
|
28
|
+
:width="size ? size : width"
|
|
29
|
+
class="es-skeleton rounded-xs mb-25 bg-gray-200"
|
|
30
|
+
:class="{
|
|
31
|
+
'es-skeleton-wave': animation == 'wave',
|
|
32
|
+
'es-skeleton-fade': animation == 'fade',
|
|
33
|
+
}" />
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<style lang="scss" scoped>
|
|
37
|
+
@use '@energysage/es-ds-styles/scss/variables' as variables;
|
|
38
|
+
|
|
39
|
+
@keyframes skeleton-animate-wave {
|
|
40
|
+
0% {
|
|
41
|
+
transform: translateX(-100%);
|
|
42
|
+
}
|
|
43
|
+
100% {
|
|
44
|
+
transform: translateX(100%);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@keyframes skeleton-animate-fade {
|
|
49
|
+
0% {
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
100% {
|
|
53
|
+
opacity: 0.4;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.es-skeleton {
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
|
|
60
|
+
&-wave:after {
|
|
61
|
+
content: '';
|
|
62
|
+
height: 100%;
|
|
63
|
+
position: absolute;
|
|
64
|
+
left: 0;
|
|
65
|
+
right: 0;
|
|
66
|
+
top: 0;
|
|
67
|
+
z-index: 1;
|
|
68
|
+
// wave animation from PrimeVue Skeleton
|
|
69
|
+
background-image: linear-gradient(
|
|
70
|
+
90deg,
|
|
71
|
+
rgba(variables.$white, 0),
|
|
72
|
+
rgba(variables.$white, 0.4),
|
|
73
|
+
rgba(variables.$white, 0)
|
|
74
|
+
);
|
|
75
|
+
animation-name: skeleton-animate-wave;
|
|
76
|
+
animation-duration: 1.2s;
|
|
77
|
+
animation-timing-function: ease;
|
|
78
|
+
animation-delay: 0s;
|
|
79
|
+
animation-iteration-count: infinite;
|
|
80
|
+
animation-direction: normal;
|
|
81
|
+
animation-fill-mode: none;
|
|
82
|
+
animation-play-state: running;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&-fade {
|
|
86
|
+
// fade animation from Bootstrap Skeleton
|
|
87
|
+
animation-name: skeleton-animate-fade;
|
|
88
|
+
animation-duration: 0.875s;
|
|
89
|
+
animation-timing-function: ease-in-out;
|
|
90
|
+
animation-delay: 0s;
|
|
91
|
+
animation-iteration-count: infinite;
|
|
92
|
+
animation-direction: alternate;
|
|
93
|
+
animation-fill-mode: none;
|
|
94
|
+
animation-play-state: running;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
</style>
|
package/components/es-slider.vue
CHANGED
|
@@ -3,20 +3,33 @@ import Slider from 'primevue/slider';
|
|
|
3
3
|
|
|
4
4
|
const props = defineProps({
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Aria label for slider handle
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
type:
|
|
10
|
-
|
|
11
|
-
default: 1,
|
|
8
|
+
ariaLabel: {
|
|
9
|
+
type: String,
|
|
10
|
+
default: null,
|
|
12
11
|
},
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
13
|
+
* An id of the element whose contents describe this slider
|
|
15
14
|
*/
|
|
16
|
-
|
|
17
|
-
type:
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
ariaLabelledby: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: null,
|
|
18
|
+
},
|
|
19
|
+
/**
|
|
20
|
+
* Indicates whether or not the slider should be disabled
|
|
21
|
+
*/
|
|
22
|
+
disabled: {
|
|
23
|
+
type: Boolean,
|
|
24
|
+
default: false,
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* Function that modifies label value
|
|
28
|
+
*/
|
|
29
|
+
labelFormatter: {
|
|
30
|
+
type: Function,
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
default: (val: any) => val,
|
|
20
33
|
},
|
|
21
34
|
/**
|
|
22
35
|
* Maximum value of slider
|
|
@@ -27,67 +40,73 @@ const props = defineProps({
|
|
|
27
40
|
default: 100,
|
|
28
41
|
},
|
|
29
42
|
/**
|
|
30
|
-
*
|
|
43
|
+
* Minimum value of slider
|
|
31
44
|
*/
|
|
32
|
-
|
|
45
|
+
min: {
|
|
33
46
|
type: Number,
|
|
47
|
+
required: true,
|
|
34
48
|
default: 1,
|
|
35
49
|
},
|
|
36
50
|
/**
|
|
37
|
-
*
|
|
51
|
+
* Starting value of slider thumb
|
|
38
52
|
*/
|
|
39
|
-
|
|
40
|
-
type:
|
|
41
|
-
|
|
42
|
-
default: 'Select a number',
|
|
53
|
+
startingValue: {
|
|
54
|
+
type: Number,
|
|
55
|
+
default: null,
|
|
43
56
|
},
|
|
44
57
|
/**
|
|
45
|
-
*
|
|
58
|
+
* Step increment
|
|
46
59
|
*/
|
|
47
|
-
|
|
48
|
-
type:
|
|
49
|
-
|
|
50
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
-
default: (val: any) => val,
|
|
60
|
+
step: {
|
|
61
|
+
type: Number,
|
|
62
|
+
default: 1,
|
|
52
63
|
},
|
|
53
64
|
/**
|
|
54
65
|
* Function that modifies tooltip value
|
|
55
66
|
*/
|
|
56
67
|
tooltipFormatter: {
|
|
57
68
|
type: Function,
|
|
58
|
-
required: false,
|
|
59
69
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
70
|
default: (val: any) => val,
|
|
61
71
|
},
|
|
62
72
|
});
|
|
63
73
|
|
|
64
74
|
// allow use of v-model on this component
|
|
75
|
+
// and only use props.startingValue if it's provided, otherwise use the value from the v-model
|
|
65
76
|
const model = defineModel<number | number[]>();
|
|
66
|
-
model.value = props.startingValue;
|
|
77
|
+
model.value = props.startingValue !== null ? props.startingValue : model.value;
|
|
67
78
|
</script>
|
|
68
79
|
|
|
69
80
|
<template>
|
|
70
|
-
<div>
|
|
81
|
+
<div :class="{ 'es-slider--disabled': props.disabled }">
|
|
71
82
|
<slider
|
|
72
83
|
v-model="model"
|
|
73
84
|
v-bind="$attrs"
|
|
85
|
+
:aria-disabled="disabled"
|
|
86
|
+
:disabled="disabled"
|
|
74
87
|
:min="min"
|
|
75
88
|
:max="max"
|
|
76
89
|
:step="step"
|
|
90
|
+
:aria-labelledby="ariaLabelledby"
|
|
77
91
|
:aria-label="ariaLabel"
|
|
78
92
|
:pt="{
|
|
79
93
|
root: {
|
|
80
|
-
class: 'slider-root',
|
|
94
|
+
class: 'es-slider-root',
|
|
81
95
|
},
|
|
82
96
|
range: {
|
|
83
|
-
class: 'slider-range',
|
|
97
|
+
class: 'es-slider-range',
|
|
84
98
|
},
|
|
85
99
|
handle: {
|
|
86
|
-
class: 'slider-handle',
|
|
100
|
+
class: 'es-slider-handle',
|
|
87
101
|
},
|
|
88
102
|
}" />
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
<!--
|
|
104
|
+
this is aria-hidden because the slider already uses the aria-valuemin and aria-value-max
|
|
105
|
+
attributes to indicate the minimum and maximum value, so these labels are just for visual users
|
|
106
|
+
-->
|
|
107
|
+
<div
|
|
108
|
+
aria-hidden
|
|
109
|
+
class="es-slider-labels d-flex flex-row font-weight-bold justify-content-between text-gray-700">
|
|
91
110
|
<span>{{ labelFormatter(min) }}</span>
|
|
92
111
|
<span>{{ labelFormatter(max) }}</span>
|
|
93
112
|
</div>
|
|
@@ -96,47 +115,74 @@ model.value = props.startingValue;
|
|
|
96
115
|
|
|
97
116
|
<style lang="scss" scoped>
|
|
98
117
|
@use '@energysage/es-ds-styles/scss/variables' as variables;
|
|
99
|
-
|
|
100
|
-
|
|
118
|
+
|
|
119
|
+
$hover-focus-animation-duration: 0.15s;
|
|
120
|
+
$slide-animation-duration: 0.05s;
|
|
121
|
+
|
|
122
|
+
:deep(.es-slider-root) {
|
|
123
|
+
background-color: variables.$gray-200;
|
|
101
124
|
border-radius: 15px;
|
|
102
125
|
height: 10px;
|
|
103
|
-
margin-bottom:
|
|
126
|
+
margin-bottom: 1rem;
|
|
104
127
|
position: relative;
|
|
128
|
+
|
|
129
|
+
&[data-p-sliding='true'] .slider-handle {
|
|
130
|
+
box-shadow: 0 0 0 9px variables.$blue-50;
|
|
131
|
+
}
|
|
105
132
|
}
|
|
106
133
|
|
|
107
|
-
:deep(.slider-range) {
|
|
108
|
-
background: linear-gradient(90deg,
|
|
134
|
+
:deep(.es-slider-range) {
|
|
135
|
+
background: linear-gradient(90deg, variables.$blue-400, variables.$blue-800);
|
|
109
136
|
border-radius: 15px;
|
|
110
137
|
height: 10px;
|
|
138
|
+
|
|
139
|
+
@media not (prefers-reduced-motion) {
|
|
140
|
+
transition: width $slide-animation-duration ease-in-out;
|
|
141
|
+
}
|
|
111
142
|
}
|
|
112
143
|
|
|
113
|
-
:deep(.slider-handle) {
|
|
144
|
+
:deep(.es-slider-handle) {
|
|
114
145
|
background-color: variables.$white;
|
|
115
|
-
border: 9px solid variables.$blue-
|
|
146
|
+
border: 9px solid variables.$blue-700;
|
|
116
147
|
border-radius: 50%;
|
|
117
|
-
box-shadow: 0
|
|
148
|
+
box-shadow: 0 1px 6px 0 rgba(34, 38, 51, 0.25);
|
|
118
149
|
cursor: pointer;
|
|
119
|
-
filter: drop-shadow(0 1px 6px rgba(34, 38, 51, 0.25));
|
|
120
150
|
height: 28px;
|
|
151
|
+
/* important to prevent sliding from attempting to horizontally scroll on mobile */
|
|
152
|
+
touch-action: none;
|
|
121
153
|
transform: translate(-54%, -32%);
|
|
122
154
|
width: 28px;
|
|
123
155
|
|
|
156
|
+
@media not (prefers-reduced-motion) {
|
|
157
|
+
transition:
|
|
158
|
+
box-shadow $hover-focus-animation-duration ease-in-out,
|
|
159
|
+
left $slide-animation-duration ease-in-out;
|
|
160
|
+
}
|
|
161
|
+
|
|
124
162
|
&:focus-visible {
|
|
125
163
|
outline: none;
|
|
126
164
|
}
|
|
165
|
+
|
|
166
|
+
&:hover {
|
|
167
|
+
box-shadow: 0 0 0 9px variables.$blue-50;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
&:focus {
|
|
171
|
+
box-shadow: 0 0 0 9px variables.$blue-300;
|
|
172
|
+
}
|
|
127
173
|
}
|
|
128
174
|
|
|
129
175
|
// "tooltip"
|
|
130
|
-
:deep(.slider-handle::before) {
|
|
176
|
+
:deep(.es-slider-handle::before) {
|
|
131
177
|
align-items: center;
|
|
132
178
|
background-color: variables.$blue-600;
|
|
133
179
|
border-radius: 50%;
|
|
134
|
-
bottom:
|
|
180
|
+
bottom: 32px;
|
|
135
181
|
box-shadow: 0 1px 6px 0 rgba(34, 38, 51, 0.25);
|
|
136
182
|
color: variables.$white;
|
|
137
183
|
content: v-bind("`'${tooltipFormatter(model)}'`");
|
|
138
184
|
display: flex;
|
|
139
|
-
font-weight: variables.$font-weight-
|
|
185
|
+
font-weight: variables.$font-weight-bold;
|
|
140
186
|
height: 52px;
|
|
141
187
|
justify-content: center;
|
|
142
188
|
padding-bottom: 2px;
|
|
@@ -144,4 +190,29 @@ model.value = props.startingValue;
|
|
|
144
190
|
right: -22px;
|
|
145
191
|
width: 52px;
|
|
146
192
|
}
|
|
193
|
+
|
|
194
|
+
.es-slider--disabled {
|
|
195
|
+
/* at least prevents click/tap from giving it focus, if not keyboard */
|
|
196
|
+
pointer-events: none;
|
|
197
|
+
|
|
198
|
+
:deep(.es-slider-range) {
|
|
199
|
+
/* use background instead of background-color here to clear out the linear-gradient */
|
|
200
|
+
background: variables.$gray-400;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
:deep(.es-slider-handle) {
|
|
204
|
+
border-color: variables.$gray-400;
|
|
205
|
+
box-shadow: none;
|
|
206
|
+
|
|
207
|
+
&::before {
|
|
208
|
+
background-color: variables.$gray-300;
|
|
209
|
+
color: variables.$gray-500;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.es-slider-labels {
|
|
214
|
+
/* use important to override the utility class */
|
|
215
|
+
color: variables.$gray-500 !important;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
147
218
|
</style>
|
package/components/es-tabs.vue
CHANGED
|
@@ -11,7 +11,7 @@ const panels = useSlots().default?.() || [];
|
|
|
11
11
|
// keep track of the active tab index
|
|
12
12
|
// - from above, if v-model is used
|
|
13
13
|
// - from below, if the tab component receives a click
|
|
14
|
-
const activeIndex = ref(0);
|
|
14
|
+
const activeIndex = ref(model.value || 0);
|
|
15
15
|
|
|
16
16
|
// watch the v-model for changes and update the active index if it does change
|
|
17
17
|
watch(model, (newVal) => {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Props
|
|
3
|
+
interface IProps {
|
|
4
|
+
altText: string;
|
|
5
|
+
coverImageUrl: string;
|
|
6
|
+
embedUrl: string;
|
|
7
|
+
}
|
|
8
|
+
const props = defineProps<IProps>();
|
|
9
|
+
const showVideo = ref(false);
|
|
10
|
+
|
|
11
|
+
// Methods
|
|
12
|
+
function getVideoIdFromUrl(url: string) {
|
|
13
|
+
const embedPattern = /https:\/\/www\.youtube\.com\/embed\/(?<videoId>[A-Za-z0-9-]+)/;
|
|
14
|
+
const parsedUrl = embedPattern.exec(url);
|
|
15
|
+
if (parsedUrl) {
|
|
16
|
+
return parsedUrl?.groups?.videoId;
|
|
17
|
+
}
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Computed
|
|
22
|
+
const videoId = computed(() => getVideoIdFromUrl(props.embedUrl));
|
|
23
|
+
const embedUrlWithParams = computed(
|
|
24
|
+
() => `https://www.youtube.com/embed/${videoId.value}?rel=0&autoplay=1&cc_load_policy=1&cc_lang_pref=en`,
|
|
25
|
+
);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<div>
|
|
30
|
+
<div
|
|
31
|
+
v-if="showVideo"
|
|
32
|
+
class="embed-responsive embed-responsive-16by9">
|
|
33
|
+
<iframe
|
|
34
|
+
:id="`youtube-video-${videoId}`"
|
|
35
|
+
:src="embedUrlWithParams"
|
|
36
|
+
class="embed-responsive-item"
|
|
37
|
+
frameborder="0"></iframe>
|
|
38
|
+
</div>
|
|
39
|
+
<es-card
|
|
40
|
+
v-else
|
|
41
|
+
:id="`youtube-placeholder-${videoId}`"
|
|
42
|
+
class="EsVideo-button bg-transparent overflow-hidden p-0 position-relative w-100"
|
|
43
|
+
variant="interactive"
|
|
44
|
+
@click="showVideo = true">
|
|
45
|
+
<slot v-if="$slots.default" />
|
|
46
|
+
<nuxt-img
|
|
47
|
+
v-else
|
|
48
|
+
class="EsVideo-image d-block w-100"
|
|
49
|
+
sizes="md:530px sm:275px"
|
|
50
|
+
:src="props.coverImageUrl"
|
|
51
|
+
:alt="props.altText" />
|
|
52
|
+
<icon-video-play
|
|
53
|
+
class="EsVideo-icon position-absolute abs-center"
|
|
54
|
+
width="74px"
|
|
55
|
+
height="54px" />
|
|
56
|
+
</es-card>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
package/nuxt.config.ts
CHANGED
package/package.json
CHANGED
|
@@ -306,6 +306,10 @@ export default (
|
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
name: 'Financing',
|
|
309
|
+
link: `${ES_DOMAIN}/financing/`,
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: 'Solar loan providers',
|
|
309
313
|
link: `${ES_DOMAIN}/solar/financing/loan-providers/`,
|
|
310
314
|
},
|
|
311
315
|
{
|
|
@@ -398,6 +402,10 @@ export default (
|
|
|
398
402
|
name: 'Heat pump incentives',
|
|
399
403
|
link: `${ES_DOMAIN}/heat-pumps/heat-pump-incentives/`,
|
|
400
404
|
},
|
|
405
|
+
{
|
|
406
|
+
name: 'Financing',
|
|
407
|
+
link: `${ES_DOMAIN}/financing/`,
|
|
408
|
+
},
|
|
401
409
|
{
|
|
402
410
|
name: 'Get personalized quotes',
|
|
403
411
|
link: `${ES_DOMAIN}/shop/heat-pumps/`,
|