@energysage/es-ds-components 0.0.1 → 3.0.0-alpha.10

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.
Files changed (234) hide show
  1. package/.eslintignore +9 -0
  2. package/.eslintrc.json +3 -0
  3. package/.prettierrc.json +10 -0
  4. package/README.md +0 -3
  5. package/components/es-accordion-list.vue +117 -0
  6. package/components/es-accordion.vue +7 -0
  7. package/components/es-badge.vue +20 -0
  8. package/components/es-breadcrumbs.vue +73 -0
  9. package/components/es-button.vue +122 -0
  10. package/components/es-card.vue +64 -0
  11. package/components/es-carousel.vue +316 -0
  12. package/components/es-col.vue +27 -0
  13. package/components/es-collapse.vue +83 -0
  14. package/components/es-container.vue +14 -0
  15. package/components/es-cta-banner.vue +95 -0
  16. package/components/es-cta-card.vue +60 -0
  17. package/components/es-data-table-simple.vue +73 -0
  18. package/components/es-data-table.vue +128 -0
  19. package/components/es-error-page.vue +120 -0
  20. package/components/es-file-input.vue +334 -0
  21. package/components/es-file-preview-modal.vue +61 -0
  22. package/components/es-footer.vue +163 -0
  23. package/components/es-form-checkbox.vue +40 -0
  24. package/components/es-form-input.vue +130 -0
  25. package/components/es-form-msg.vue +81 -0
  26. package/components/es-form-radio-card.vue +57 -0
  27. package/components/es-form-radio-cards.vue +78 -0
  28. package/components/es-form-textarea.vue +124 -0
  29. package/components/es-form.vue +16 -0
  30. package/components/es-modal.vue +103 -0
  31. package/components/es-nav-bar-account-menu.vue +70 -0
  32. package/components/es-nav-bar-featured-article.vue +60 -0
  33. package/components/es-nav-bar-link.vue +27 -0
  34. package/components/es-nav-bar-product-menu.vue +219 -0
  35. package/components/es-nav-bar-top-level-menu.vue +194 -0
  36. package/components/es-nav-bar-topic-menu.vue +197 -0
  37. package/components/es-nav-bar.vue +411 -0
  38. package/components/es-pagination.vue +67 -0
  39. package/components/es-popover.vue +113 -0
  40. package/components/es-progress-circle.vue +81 -0
  41. package/components/es-progress.vue +36 -0
  42. package/components/es-radio-button-group.vue +53 -0
  43. package/components/es-radio-button.vue +57 -0
  44. package/components/es-rating.vue +120 -0
  45. package/components/es-reviews-io-card-carousel.vue +279 -0
  46. package/components/es-row.vue +5 -0
  47. package/components/es-skeleton-img.vue +38 -0
  48. package/components/es-skeleton-wrapper.vue +17 -0
  49. package/components/es-skeleton.vue +97 -0
  50. package/components/es-slider.vue +218 -0
  51. package/components/es-spinner.vue +22 -0
  52. package/components/es-support-card.vue +150 -0
  53. package/components/es-support.vue +66 -0
  54. package/components/es-tab.vue +7 -0
  55. package/components/es-tabs.vue +70 -0
  56. package/components/es-verification-code.vue +94 -0
  57. package/components/es-video.vue +60 -0
  58. package/components/es-view-more.vue +64 -0
  59. package/components/es-zip-code-form.vue +204 -0
  60. package/components/icon/arrow-clockwise.vue +51 -0
  61. package/components/icon/arrow-counter-clockwise.vue +37 -0
  62. package/components/icon/arrow-cycle.vue +43 -0
  63. package/components/icon/arrow-left.vue +39 -0
  64. package/components/icon/arrow-right.vue +39 -0
  65. package/components/icon/arrow-up.vue +39 -0
  66. package/components/icon/arrow.vue +68 -0
  67. package/components/icon/bank.vue +38 -0
  68. package/components/icon/battery.vue +40 -0
  69. package/components/icon/bell.vue +42 -0
  70. package/components/icon/block.vue +46 -0
  71. package/components/icon/bolt.vue +38 -0
  72. package/components/icon/business.vue +42 -0
  73. package/components/icon/calculator.vue +37 -0
  74. package/components/icon/calendar.vue +46 -0
  75. package/components/icon/cart.vue +46 -0
  76. package/components/icon/chat-add.vue +44 -0
  77. package/components/icon/chat-approve.vue +42 -0
  78. package/components/icon/chat-bubble.vue +40 -0
  79. package/components/icon/chat-dots.vue +42 -0
  80. package/components/icon/check.vue +39 -0
  81. package/components/icon/chevron-down.vue +39 -0
  82. package/components/icon/chevron-horizontal.vue +39 -0
  83. package/components/icon/chevron-left.vue +39 -0
  84. package/components/icon/chevron-right.vue +39 -0
  85. package/components/icon/chevron-up.vue +39 -0
  86. package/components/icon/circle-alert.vue +46 -0
  87. package/components/icon/circle-check.vue +46 -0
  88. package/components/icon/circle-chevron-down.vue +47 -0
  89. package/components/icon/circle-chevron-left.vue +46 -0
  90. package/components/icon/circle-chevron-right.vue +46 -0
  91. package/components/icon/circle-chevron-up.vue +46 -0
  92. package/components/icon/circle-minus.vue +46 -0
  93. package/components/icon/circle-plus.vue +46 -0
  94. package/components/icon/circle-x.vue +46 -0
  95. package/components/icon/circle.vue +42 -0
  96. package/components/icon/clock.vue +46 -0
  97. package/components/icon/community-solar.vue +37 -0
  98. package/components/icon/companies.vue +43 -0
  99. package/components/icon/contact-us.vue +37 -0
  100. package/components/icon/copy.vue +45 -0
  101. package/components/icon/credit-card.vue +37 -0
  102. package/components/icon/document.vue +45 -0
  103. package/components/icon/electric-vehicle.vue +37 -0
  104. package/components/icon/electricity-circle.vue +39 -0
  105. package/components/icon/envelope.vue +45 -0
  106. package/components/icon/es-leaf.vue +39 -0
  107. package/components/icon/eye.vue +41 -0
  108. package/components/icon/facebook.vue +41 -0
  109. package/components/icon/file-doc.vue +72 -0
  110. package/components/icon/file-docx.vue +72 -0
  111. package/components/icon/file-pdf.vue +72 -0
  112. package/components/icon/flag.vue +37 -0
  113. package/components/icon/folder.vue +41 -0
  114. package/components/icon/gear.vue +37 -0
  115. package/components/icon/gift.vue +37 -0
  116. package/components/icon/hamburger.vue +38 -0
  117. package/components/icon/heart.vue +39 -0
  118. package/components/icon/heat-pump.vue +41 -0
  119. package/components/icon/image.vue +45 -0
  120. package/components/icon/info-blog-post.vue +47 -0
  121. package/components/icon/info.vue +45 -0
  122. package/components/icon/instagram.vue +49 -0
  123. package/components/icon/light-bulb.vue +36 -0
  124. package/components/icon/link-out.vue +38 -0
  125. package/components/icon/linkedin.vue +41 -0
  126. package/components/icon/location.vue +45 -0
  127. package/components/icon/lock-off.vue +45 -0
  128. package/components/icon/lock-on.vue +48 -0
  129. package/components/icon/marketplace.vue +43 -0
  130. package/components/icon/minus.vue +38 -0
  131. package/components/icon/money-bag.vue +37 -0
  132. package/components/icon/money.vue +35 -0
  133. package/components/icon/open-envelope.vue +45 -0
  134. package/components/icon/paper.vue +45 -0
  135. package/components/icon/payback.vue +37 -0
  136. package/components/icon/pencil.vue +45 -0
  137. package/components/icon/person-add.vue +41 -0
  138. package/components/icon/person-check.vue +41 -0
  139. package/components/icon/person-cross.vue +41 -0
  140. package/components/icon/person.vue +41 -0
  141. package/components/icon/phone.vue +41 -0
  142. package/components/icon/play.vue +41 -0
  143. package/components/icon/plus.vue +38 -0
  144. package/components/icon/roof.vue +37 -0
  145. package/components/icon/rooftop-solar.vue +37 -0
  146. package/components/icon/saving.vue +37 -0
  147. package/components/icon/search.vue +43 -0
  148. package/components/icon/settings-horizontal.vue +57 -0
  149. package/components/icon/settings-vertical.vue +57 -0
  150. package/components/icon/shield-with-check.vue +38 -0
  151. package/components/icon/shield.vue +41 -0
  152. package/components/icon/shopping-bag.vue +39 -0
  153. package/components/icon/solar.vue +37 -0
  154. package/components/icon/sound-down.vue +45 -0
  155. package/components/icon/sound-off.vue +41 -0
  156. package/components/icon/sound-on.vue +41 -0
  157. package/components/icon/sound-up.vue +41 -0
  158. package/components/icon/star-empty.vue +38 -0
  159. package/components/icon/star-full.vue +36 -0
  160. package/components/icon/star-half.vue +55 -0
  161. package/components/icon/state-ak.vue +53 -0
  162. package/components/icon/state-al.vue +38 -0
  163. package/components/icon/state-ar.vue +38 -0
  164. package/components/icon/state-az.vue +38 -0
  165. package/components/icon/state-ca.vue +38 -0
  166. package/components/icon/state-co.vue +38 -0
  167. package/components/icon/state-ct.vue +38 -0
  168. package/components/icon/state-dc.vue +52 -0
  169. package/components/icon/state-de.vue +38 -0
  170. package/components/icon/state-fl.vue +38 -0
  171. package/components/icon/state-ga.vue +38 -0
  172. package/components/icon/state-hi.vue +59 -0
  173. package/components/icon/state-ia.vue +38 -0
  174. package/components/icon/state-id.vue +38 -0
  175. package/components/icon/state-il.vue +38 -0
  176. package/components/icon/state-in.vue +38 -0
  177. package/components/icon/state-ks.vue +38 -0
  178. package/components/icon/state-ky.vue +38 -0
  179. package/components/icon/state-la.vue +38 -0
  180. package/components/icon/state-ma.vue +38 -0
  181. package/components/icon/state-md.vue +39 -0
  182. package/components/icon/state-me.vue +38 -0
  183. package/components/icon/state-mi.vue +38 -0
  184. package/components/icon/state-mn.vue +38 -0
  185. package/components/icon/state-mo.vue +38 -0
  186. package/components/icon/state-ms.vue +38 -0
  187. package/components/icon/state-mt.vue +38 -0
  188. package/components/icon/state-nc.vue +38 -0
  189. package/components/icon/state-nd.vue +38 -0
  190. package/components/icon/state-ne.vue +38 -0
  191. package/components/icon/state-nh.vue +38 -0
  192. package/components/icon/state-nj.vue +37 -0
  193. package/components/icon/state-nm.vue +38 -0
  194. package/components/icon/state-nv.vue +38 -0
  195. package/components/icon/state-ny.vue +41 -0
  196. package/components/icon/state-oh.vue +38 -0
  197. package/components/icon/state-ok.vue +38 -0
  198. package/components/icon/state-or.vue +38 -0
  199. package/components/icon/state-pa.vue +38 -0
  200. package/components/icon/state-ri.vue +38 -0
  201. package/components/icon/state-sc.vue +38 -0
  202. package/components/icon/state-sd.vue +38 -0
  203. package/components/icon/state-tn.vue +38 -0
  204. package/components/icon/state-tx.vue +38 -0
  205. package/components/icon/state-ut.vue +38 -0
  206. package/components/icon/state-va.vue +38 -0
  207. package/components/icon/state-vt.vue +38 -0
  208. package/components/icon/state-wa.vue +38 -0
  209. package/components/icon/state-wi.vue +38 -0
  210. package/components/icon/state-wv.vue +38 -0
  211. package/components/icon/state-wy.vue +38 -0
  212. package/components/icon/thumbs-up.vue +39 -0
  213. package/components/icon/trash-can.vue +41 -0
  214. package/components/icon/tree.vue +37 -0
  215. package/components/icon/twitter.vue +43 -0
  216. package/components/icon/upload.vue +41 -0
  217. package/components/icon/verified.vue +40 -0
  218. package/components/icon/video-play.vue +41 -0
  219. package/components/icon/wifi.vue +38 -0
  220. package/components/icon/x.vue +38 -0
  221. package/components/icon/youtube.vue +45 -0
  222. package/composables/forms.ts +124 -0
  223. package/nuxt.config.ts +83 -1
  224. package/package.json +47 -13
  225. package/tsconfig.json +4 -0
  226. package/types/es-carousel.ts +12 -0
  227. package/types/index.ts +1 -0
  228. package/utils/forms.ts +120 -0
  229. package/utils/get-es-footer-content.ts +90 -0
  230. package/utils/get-es-nav-bar-account-content.ts +55 -0
  231. package/utils/get-es-nav-bar-global-content.ts +482 -0
  232. package/utils/mime-type-finder.ts +48 -0
  233. package/utils/vuelidate-keys.ts +39 -0
  234. package/components/TestComponent.vue +0 -5
@@ -0,0 +1,316 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ TODO:
4
+ - responsive carousels briefly show mobile amount of dots on desktop, until hydration
5
+ - i think we may be able to fix this with CSS, because we know how many dots there should be
6
+ - (something PrimeVue Carousel should really be doing)
7
+ - circular has quirky behavior when numVisible doesn't match numScroll
8
+ - you can see this in the circular autoplay example
9
+ - i'm not sure if this is fixable
10
+ */
11
+
12
+ import Carousel from 'primevue/carousel';
13
+ import sassBreakpoints from '@energysage/es-ds-styles/scss/modules/breakpoints.module.scss';
14
+ import type { EsCarouselBreakpointsInterface } from '../types';
15
+
16
+ interface IProps {
17
+ autoPlay?: boolean;
18
+ autoPlayInterval?: number;
19
+ breakpoints?: EsCarouselBreakpointsInterface;
20
+ circular?: boolean;
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ items: Array<any>;
23
+ numScroll?: number;
24
+ numVisible?: number;
25
+ showArrows?: boolean;
26
+ showDots?: boolean;
27
+ }
28
+ const props = withDefaults(defineProps<IProps>(), {
29
+ autoPlay: false,
30
+ autoPlayInterval: 2000,
31
+ breakpoints: () => ({}),
32
+ circular: false,
33
+ items: () => [],
34
+ numScroll: 1,
35
+ numVisible: 1,
36
+ showArrows: true,
37
+ showDots: true,
38
+ });
39
+
40
+ const emit = defineEmits<{
41
+ update: [value: number];
42
+ }>();
43
+
44
+ // get the breakpoint pixel numbers from the SASS variables in es-ds-styles
45
+ const parseSassBreakpoint = (breakpoint: string) => parseInt(breakpoint.replace('px', ''), 10);
46
+ const BREAKPOINTS = {
47
+ SM: parseSassBreakpoint(sassBreakpoints.sm),
48
+ MD: parseSassBreakpoint(sassBreakpoints.md),
49
+ LG: parseSassBreakpoint(sassBreakpoints.lg),
50
+ XL: parseSassBreakpoint(sassBreakpoints.xl),
51
+ XXL: parseSassBreakpoint(sassBreakpoints.xxl),
52
+ };
53
+
54
+ // lower breakpoint values propagate to higher breakpoints unless overridden
55
+ const numVisibleXs = computed(() => props.numVisible);
56
+ const numVisibleSm = computed(() => props.breakpoints?.sm?.numVisible || numVisibleXs.value);
57
+ const numVisibleMd = computed(() => props.breakpoints?.md?.numVisible || numVisibleSm.value);
58
+ const numVisibleLg = computed(() => props.breakpoints?.lg?.numVisible || numVisibleMd.value);
59
+ const numVisibleXl = computed(() => props.breakpoints?.xl?.numVisible || numVisibleLg.value);
60
+ const numVisibleXxl = computed(() => props.breakpoints?.xxl?.numVisible || numVisibleXl.value);
61
+
62
+ // lower breakpoint values propagate to higher breakpoints unless overridden
63
+ const numScrollXs = computed(() => props.numScroll);
64
+ const numScrollSm = computed(() => props.breakpoints?.sm?.numScroll || numScrollXs.value);
65
+ const numScrollMd = computed(() => props.breakpoints?.md?.numScroll || numScrollSm.value);
66
+ const numScrollLg = computed(() => props.breakpoints?.lg?.numScroll || numScrollMd.value);
67
+ const numScrollXl = computed(() => props.breakpoints?.xl?.numScroll || numScrollLg.value);
68
+ const numScrollXxl = computed(() => props.breakpoints?.xxl?.numScroll || numScrollXl.value);
69
+
70
+ // in order to enable full-width carousels with one item
71
+ // but also ensure there is some space between items when more than one is visible
72
+ // only apply horizontal padding to carousel items on breakpoints where more than one item is visible
73
+ const DEFAULT_PADDING = '0.5rem';
74
+ const paddingXs = computed(() => (numVisibleXs.value === 1 ? '0' : DEFAULT_PADDING));
75
+ const paddingSm = computed(() => (numVisibleSm.value === 1 ? '0' : DEFAULT_PADDING));
76
+ const paddingMd = computed(() => (numVisibleMd.value === 1 ? '0' : DEFAULT_PADDING));
77
+ const paddingLg = computed(() => (numVisibleLg.value === 1 ? '0' : DEFAULT_PADDING));
78
+ const paddingXl = computed(() => (numVisibleXl.value === 1 ? '0' : DEFAULT_PADDING));
79
+ const paddingXxl = computed(() => (numVisibleXxl.value === 1 ? '0' : DEFAULT_PADDING));
80
+
81
+ const responsiveOptions = computed(() => {
82
+ // if no special breakpoints are defined, don't pass in any responsive options
83
+ if (!Object.keys(props.breakpoints).length) {
84
+ return undefined;
85
+ }
86
+
87
+ return [
88
+ // XXL breakpoint
89
+ {
90
+ // max width of XXL is infinite, so let's use 9999px
91
+ breakpoint: '9999px',
92
+ numScroll: numScrollXxl.value,
93
+ numVisible: numVisibleXxl.value,
94
+ },
95
+ // XL breakpoint
96
+ {
97
+ // max width of XL is XXL minus one
98
+ breakpoint: `${BREAKPOINTS.XXL - 1}px`,
99
+ numScroll: numScrollXl.value,
100
+ numVisible: numVisibleXl.value,
101
+ },
102
+ // LG breakpoint
103
+ {
104
+ // max width of LG is XL minus one
105
+ breakpoint: `${BREAKPOINTS.XL - 1}px`,
106
+ numScroll: numScrollLg.value,
107
+ numVisible: numVisibleLg.value,
108
+ },
109
+ // MD breakpoint
110
+ {
111
+ // max width of MD is LG minus one
112
+ breakpoint: `${BREAKPOINTS.LG - 1}px`,
113
+ numScroll: numScrollMd.value,
114
+ numVisible: numVisibleMd.value,
115
+ },
116
+ // SM breakpoint
117
+ {
118
+ // max width of SM is MD minus one
119
+ breakpoint: `${BREAKPOINTS.MD - 1}px`,
120
+ numScroll: numScrollSm.value,
121
+ numVisible: numVisibleSm.value,
122
+ },
123
+ // XS breakpoint
124
+ // (this is necessary to avoid weird behavior on mobile)
125
+ {
126
+ // max width of XS is SM minus one
127
+ breakpoint: `${BREAKPOINTS.SM - 1}px`,
128
+ numScroll: props.numScroll,
129
+ numVisible: props.numVisible,
130
+ },
131
+ ];
132
+ });
133
+
134
+ const autoplayInterval = ref(props.autoPlay ? props.autoPlayInterval : 0);
135
+ const isMounted = ref(false);
136
+ const key = ref('');
137
+
138
+ const stopAutoplay = () => {
139
+ if (autoplayInterval.value > 0) {
140
+ autoplayInterval.value = 0;
141
+ key.value = 'stopAutoplay';
142
+ }
143
+ };
144
+
145
+ onMounted(() => {
146
+ /**
147
+ * avoids an unavoidable SSR issue with responsive carousels (SSR can't know the breakpoint
148
+ * and therefore how many items need to be displayed per page) where circular carousels have
149
+ * their last few items cloned and inserted before the first item in the list, meaning on initial
150
+ * page render, you see those cloned last items listed first rather than the first item.
151
+ *
152
+ * then, upon hydration, the items change and the first item is then listed first.
153
+ *
154
+ * this workaround disables circular functionality for SSR, and swaps to the user-provided
155
+ * setting for it on mount.
156
+ */
157
+ isMounted.value = true;
158
+
159
+ document.addEventListener('keyup', (e) => {
160
+ if (e.key === 'Escape') {
161
+ // Stop carousel when user presses Escape key, in lieu of pause button
162
+ // https://www.w3.org/WAI/WCAG22/Techniques/general/G187.html
163
+ stopAutoplay();
164
+ }
165
+ });
166
+ });
167
+ </script>
168
+
169
+ <template>
170
+ <carousel
171
+ :key="key"
172
+ :autoplay-interval="autoplayInterval"
173
+ :circular="isMounted && props.circular"
174
+ :num-scroll="props.numScroll"
175
+ :num-visible="props.numVisible"
176
+ :responsive-options="responsiveOptions"
177
+ :show-indicators="props.showDots"
178
+ :show-navigators="props.showArrows"
179
+ :value="props.items"
180
+ :pt="{
181
+ container: {
182
+ class: 'd-flex',
183
+ },
184
+ indicator: {
185
+ class: 'es-carousel-dot',
186
+ },
187
+ indicators: {
188
+ class: 'es-carousel-dots d-flex justify-content-center mt-100',
189
+ },
190
+ itemsContent: {
191
+ class: 'w-100 overflow-hidden',
192
+ },
193
+ itemsContainer: {
194
+ class: 'd-flex',
195
+ },
196
+ item: {
197
+ class: 'es-carousel-item',
198
+ },
199
+ itemCloned: {
200
+ class: 'es-carousel-item',
201
+ },
202
+ previousButton: {
203
+ class: 'es-carousel-arrow es-carousel-prev-arrow btn btn-outline-primary px-sm-50',
204
+ },
205
+ nextButton: {
206
+ class: 'es-carousel-arrow es-carousel-next-arrow btn btn-outline-primary px-sm-50',
207
+ },
208
+ }"
209
+ @update:page="(value: number) => emit('update', value)">
210
+ <template #item="item">
211
+ <slot
212
+ name="item"
213
+ :item="item.data" />
214
+ </template>
215
+ <template #previousicon>
216
+ <icon-chevron-left />
217
+ </template>
218
+ <template #nexticon>
219
+ <icon-chevron-right />
220
+ </template>
221
+ </carousel>
222
+ </template>
223
+
224
+ <style lang="scss" scoped>
225
+ @use '@energysage/es-ds-styles/scss/variables' as variables;
226
+ @use '@energysage/es-ds-styles/scss/mixins/breakpoints' as breakpoints;
227
+
228
+ :deep(.es-carousel-item) {
229
+ flex: 1 0 calc(100% / v-bind(numVisibleXs));
230
+ padding: 0 v-bind(paddingXs);
231
+ }
232
+
233
+ @include breakpoints.media-breakpoint-up(sm) {
234
+ :deep(.es-carousel-item) {
235
+ flex: 1 0 calc(100% / v-bind(numVisibleSm));
236
+ padding: 0 v-bind(paddingSm);
237
+ }
238
+ }
239
+
240
+ @include breakpoints.media-breakpoint-up(md) {
241
+ :deep(.es-carousel-item) {
242
+ flex: 1 0 calc(100% / v-bind(numVisibleMd));
243
+ padding: 0 v-bind(paddingMd);
244
+ }
245
+ }
246
+
247
+ @include breakpoints.media-breakpoint-up(lg) {
248
+ :deep(.es-carousel-item) {
249
+ flex: 1 0 calc(100% / v-bind(numVisibleLg));
250
+ padding: 0 v-bind(paddingLg);
251
+ }
252
+ }
253
+
254
+ @include breakpoints.media-breakpoint-up(xl) {
255
+ :deep(.es-carousel-item) {
256
+ flex: 1 0 calc(100% / v-bind(numVisibleXl));
257
+ padding: 0 v-bind(paddingXl);
258
+ }
259
+ }
260
+
261
+ @include breakpoints.media-breakpoint-up(xxl) {
262
+ :deep(.es-carousel-item) {
263
+ flex: 1 0 calc(100% / v-bind(numVisibleXxl));
264
+ padding: 0 v-bind(paddingXxl);
265
+ }
266
+ }
267
+
268
+ :deep(.es-carousel-arrow) {
269
+ background: unset;
270
+ border: unset;
271
+ box-shadow: none;
272
+ color: variables.$gray-900;
273
+ height: auto;
274
+
275
+ &:hover {
276
+ color: variables.$gray-700;
277
+ }
278
+ &:focus {
279
+ color: variables.$gray-900;
280
+ }
281
+ &:not(:disabled):not(.disabled):active {
282
+ background: unset;
283
+ box-shadow: none;
284
+ color: variables.$gray-700;
285
+ }
286
+ &:disabled {
287
+ color: variables.$gray-400;
288
+ }
289
+ }
290
+
291
+ :deep(.es-carousel-dots) {
292
+ gap: 0.7rem;
293
+ padding-left: 0;
294
+ }
295
+
296
+ :deep(.es-carousel-dot) {
297
+ list-style-type: none;
298
+ margin-bottom: 0;
299
+
300
+ &[data-p-highlight='true'] button {
301
+ background-color: variables.$orange-800;
302
+ }
303
+
304
+ button {
305
+ background-color: variables.$gray-100;
306
+ border: none;
307
+ border-radius: 50%;
308
+ height: 0.875rem;
309
+ width: 0.875rem;
310
+
311
+ &:hover {
312
+ opacity: 0.8;
313
+ }
314
+ }
315
+ }
316
+ </style>
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ cols?: string;
4
+ sm?: string;
5
+ md?: string;
6
+ lg?: string;
7
+ xl?: string;
8
+ xxl?: string;
9
+ }>();
10
+
11
+ const undefinedSize = !(props.cols || props.sm || props.md || props.lg || props.xl || props.xxl);
12
+ </script>
13
+
14
+ <template>
15
+ <div
16
+ :class="{
17
+ col: undefinedSize,
18
+ [`col-${cols}`]: cols,
19
+ [`col-sm${sm ? `-${sm}` : ''}`]: sm !== undefined,
20
+ [`col-md${md ? `-${md}` : ''}`]: md !== undefined,
21
+ [`col-lg${lg ? `-${lg}` : ''}`]: lg !== undefined,
22
+ [`col-xl${xl ? `-${xl}` : ''}`]: xl !== undefined,
23
+ [`col-xxl${xxl ? `-${xxl}` : ''}`]: xxl !== undefined,
24
+ }">
25
+ <slot />
26
+ </div>
27
+ </template>
@@ -0,0 +1,83 @@
1
+ <script setup lang="ts">
2
+ import Panel from 'primevue/panel';
3
+
4
+ const emit = defineEmits(['toggled', 'userClick']);
5
+
6
+ const model = defineModel<boolean>();
7
+ const props = defineProps({
8
+ border: {
9
+ type: Boolean,
10
+ default: true,
11
+ },
12
+ isProgrammaticUntilUserInput: {
13
+ type: Boolean,
14
+ default: true,
15
+ },
16
+ visible: {
17
+ type: Boolean,
18
+ default: false,
19
+ },
20
+ });
21
+
22
+ const userDeterminedState: Ref<boolean | null> = ref(null);
23
+
24
+ // directly determines the expanded/collapsed state of the panel
25
+ const expanded = computed(() => {
26
+ // if we are meant to start ignoring the programmatic incoming state as soon as the
27
+ // user interacts with the collapse, and if the user has interacted with the collapse,
28
+ // use the value from the user's interaction
29
+ if (props.isProgrammaticUntilUserInput && userDeterminedState.value !== null) {
30
+ return userDeterminedState.value;
31
+ }
32
+
33
+ // otherwise, use the one-way visible prop or the two-way v-model value
34
+ return props.visible || model.value;
35
+ });
36
+
37
+ const onClick = ({ value }: { value: boolean }) => {
38
+ // save the state toggled by the user
39
+ // convert from "collapsed" semantics internal to the primevue panel
40
+ // to the "expanded" semantics exposed by this component
41
+ userDeterminedState.value = !value;
42
+
43
+ // inform the v-model (if any) that the state has changed
44
+ model.value = !value;
45
+
46
+ // emit events indicating the state has changed
47
+ emit('toggled', !value);
48
+ emit('userClick', !value);
49
+ };
50
+ </script>
51
+
52
+ <template>
53
+ <panel
54
+ :collapsed="!expanded"
55
+ :pt="{
56
+ header: 'align-items-center d-flex justify-content-between position-relative py-100',
57
+ icons: 'h-100 position-absolute w-100',
58
+ root: border ? 'border-bottom border-top' : '',
59
+ toggler: {
60
+ // eslint-disable-next-line max-len
61
+ class: 'es-collapse-toggler align-items-center btn btn-link d-flex h-100 inline justify-content-end rounded-0 p-0 position-absolute text-body w-100',
62
+ },
63
+ // https://vuejs.org/guide/built-ins/transition
64
+ transition: {
65
+ enterActiveClass: 'es-collapse-enter-active',
66
+ enterFromClass: 'es-collapse-enter-from',
67
+ enterToClass: 'es-collapse-enter-to',
68
+ leaveActiveClass: 'es-collapse-leave-active',
69
+ leaveFromClass: 'es-collapse-leave-from',
70
+ leaveToClass: 'es-collapse-leave-to',
71
+ },
72
+ }"
73
+ toggleable
74
+ @toggle="onClick">
75
+ <template #header>
76
+ <slot name="title" />
77
+ </template>
78
+ <template #togglericon>
79
+ <icon-chevron-down />
80
+ </template>
81
+ <slot />
82
+ </panel>
83
+ </template>
@@ -0,0 +1,14 @@
1
+ <script setup lang="ts">
2
+ interface IProps {
3
+ fluid?: boolean;
4
+ }
5
+ const props = withDefaults(defineProps<IProps>(), {
6
+ fluid: false,
7
+ });
8
+ </script>
9
+
10
+ <template>
11
+ <div :class="props.fluid ? 'container-fluid' : 'container'">
12
+ <slot />
13
+ </div>
14
+ </template>
@@ -0,0 +1,95 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps({
3
+ dark: {
4
+ type: Boolean,
5
+ default: false,
6
+ },
7
+ hasButton: {
8
+ type: Boolean,
9
+ default: false,
10
+ },
11
+ // default or wide
12
+ variant: {
13
+ type: String,
14
+ default: 'default',
15
+ },
16
+ });
17
+
18
+ const lgFirst = computed(() => {
19
+ if (props.variant === 'wide') {
20
+ return props.hasButton ? '8' : '6';
21
+ }
22
+
23
+ return props.hasButton ? '7' : '4';
24
+ });
25
+
26
+ const lgSecond = computed(() => {
27
+ if (props.variant === 'wide') {
28
+ return props.hasButton ? '4' : '6';
29
+ }
30
+
31
+ return props.hasButton ? '5' : '8';
32
+ });
33
+
34
+ const xxlFirst = computed(() => {
35
+ if (props.variant === 'wide') {
36
+ return props.hasButton ? '8' : '6';
37
+ }
38
+
39
+ return props.hasButton ? '8' : '5';
40
+ });
41
+
42
+ const xxlSecond = computed(() => {
43
+ if (props.variant === 'wide') {
44
+ return props.hasButton ? '4' : '6';
45
+ }
46
+
47
+ return props.hasButton ? '4' : '7';
48
+ });
49
+ </script>
50
+
51
+ <template>
52
+ <es-card
53
+ :class="[
54
+ {
55
+ 'bg-dark-blue border-dark-blue text-white': dark,
56
+ 'px-100 px-lg-200': variant === 'default',
57
+ 'px-100 px-lg-300': variant === 'wide',
58
+ },
59
+ ]"
60
+ v-bind="$attrs">
61
+ <es-row>
62
+ <es-col
63
+ class="mb-200 my-lg-auto text-center text-lg-left"
64
+ :lg="lgFirst"
65
+ :xxl="xxlFirst">
66
+ <!-- avoiding use of an <h2> tag here for long-form content SEO reasons,
67
+ but preserving heading semantics for screen readers -->
68
+ <div
69
+ role="heading"
70
+ aria-level="2"
71
+ class="font-weight-semibold"
72
+ :class="{
73
+ 'font-size-300': variant === 'default',
74
+ 'mb-50': $slots.subtitle,
75
+ 'mb-0': !$slots.subtitle,
76
+ 'text-white': dark,
77
+ h2: variant === 'wide',
78
+ }">
79
+ <slot name="heading"> Easily find what solar costs in your area </slot>
80
+ </div>
81
+ <p
82
+ v-if="$slots.subtitle"
83
+ class="mb-0">
84
+ <slot name="subtitle" />
85
+ </p>
86
+ </es-col>
87
+ <es-col
88
+ class="d-flex justify-content-center justify-content-lg-end my-auto"
89
+ :lg="lgSecond"
90
+ :xxl="xxlSecond">
91
+ <slot name="cta" />
92
+ </es-col>
93
+ </es-row>
94
+ </es-card>
95
+ </template>
@@ -0,0 +1,60 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps({
3
+ imageFirst: {
4
+ type: Boolean,
5
+ default: false,
6
+ },
7
+ horizontalPadding: {
8
+ type: String,
9
+ default: 'px-100',
10
+ },
11
+ verticalSpacing: {
12
+ type: String,
13
+ default: 'mb-150',
14
+ },
15
+ });
16
+ </script>
17
+
18
+ <template>
19
+ <es-card
20
+ class="align-items-center d-flex flex-column py-200 text-center"
21
+ :class="props.horizontalPadding"
22
+ v-bind="$attrs">
23
+ <!-- avoiding use of an <h2> tag here for long-form content SEO reasons,
24
+ but preserving heading semantics for screen readers -->
25
+ <div
26
+ role="heading"
27
+ aria-level="2"
28
+ class="font-weight-semibold font-size-300"
29
+ :class="[{ 'order-1': props.imageFirst }, props.verticalSpacing]">
30
+ <slot name="heading"> Easily find what solar costs in your area </slot>
31
+ </div>
32
+ <div
33
+ v-if="$slots.image"
34
+ class="mx-auto w-100"
35
+ :class="props.verticalSpacing">
36
+ <slot name="image" />
37
+ </div>
38
+ <div
39
+ class="w-100"
40
+ :class="{ 'order-2': props.imageFirst }">
41
+ <div
42
+ v-if="$slots.subtitle"
43
+ class="es-cta-card-subtitle"
44
+ :class="props.verticalSpacing">
45
+ <slot name="subtitle" />
46
+ </div>
47
+ <slot name="cta" />
48
+ </div>
49
+ </es-card>
50
+ </template>
51
+
52
+ <style lang="scss" scoped>
53
+ .es-cta-card {
54
+ &-subtitle {
55
+ :deep(p:last-child) {
56
+ margin-bottom: 0;
57
+ }
58
+ }
59
+ }
60
+ </style>
@@ -0,0 +1,73 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps({
3
+ fields: {
4
+ type: Array,
5
+ default: null,
6
+ },
7
+ items: {
8
+ type: Array,
9
+ default: () => [],
10
+ },
11
+ tableClass: {
12
+ type: [Array, String],
13
+ default: null,
14
+ },
15
+ striped: {
16
+ type: Boolean,
17
+ default: false,
18
+ },
19
+ });
20
+
21
+ const isSingleCol = computed(() => !props.fields);
22
+ const computedItems: ComputedRef<any[]> = computed(() => {
23
+ if (!props.items.length && !useSlots().default) {
24
+ throw new Error('Neither items prop nor slot content is defined.');
25
+ }
26
+ // If there are no fields, just return the items as a single-column array
27
+ if (!props.fields) {
28
+ return props.items;
29
+ }
30
+ if (props.fields.length !== props.items.length) {
31
+ throw new Error('The number of fields and items must be the same.');
32
+ }
33
+ // If fields and items are both present, map them together
34
+ return props.fields.map((field, index) => [field, props.items[index]]);
35
+ });
36
+ </script>
37
+ <template>
38
+ <div
39
+ :class="tableClass"
40
+ class="table-responsive">
41
+ <table
42
+ v-if="$slots.default || items.length"
43
+ class="table table-borderless"
44
+ :class="{ 'table-striped': striped }">
45
+ <template v-if="$slots.default">
46
+ <slot />
47
+ </template>
48
+ <tbody v-else>
49
+ <template v-if="isSingleCol">
50
+ <tr
51
+ v-for="(item, index) in computedItems"
52
+ :key="`${index}${item}`">
53
+ <td>
54
+ {{ item }}
55
+ </td>
56
+ </tr>
57
+ </template>
58
+ <template v-else>
59
+ <tr
60
+ v-for="(item, index) in computedItems"
61
+ :key="`${index}${item[0]}`">
62
+ <td class="col-sm-7">
63
+ {{ item[0] }}
64
+ </td>
65
+ <td class="col-sm-4">
66
+ <b>{{ item[1] }}</b>
67
+ </td>
68
+ </tr>
69
+ </template>
70
+ </tbody>
71
+ </table>
72
+ </div>
73
+ </template>