@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,150 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps({
3
+ constrained: {
4
+ type: Boolean,
5
+ default: false,
6
+ },
7
+ imageAltText: {
8
+ type: String,
9
+ default: '',
10
+ },
11
+ imageSrc: {
12
+ type: String,
13
+ default: '',
14
+ },
15
+ primaryCtaTarget: {
16
+ type: String,
17
+ default: '_blank',
18
+ },
19
+ primaryCtaText: {
20
+ type: String,
21
+ required: true,
22
+ },
23
+ primaryCtaUrl: {
24
+ type: String,
25
+ required: true,
26
+ },
27
+ secondaryCtaTarget: {
28
+ type: String,
29
+ default: '_blank',
30
+ },
31
+ secondaryCtaText: {
32
+ type: String,
33
+ default: '',
34
+ },
35
+ secondaryCtaUrl: {
36
+ type: String,
37
+ default: '',
38
+ },
39
+ });
40
+
41
+ const showSecondaryCta = computed(() => {
42
+ return !!props.secondaryCtaText && !!props.secondaryCtaUrl;
43
+ });
44
+ </script>
45
+
46
+ <template>
47
+ <es-card
48
+ class="p-100 px-sm-200 text-center text-lg-left"
49
+ :class="{
50
+ 'px-md-100 py-md-200': constrained,
51
+ 'px-md-300': !constrained,
52
+ }">
53
+ <es-row class="align-items-lg-center justify-content-xl-between">
54
+ <es-col
55
+ class="EsSupportCard-contentColumn d-flex flex-column justify-content-lg-center position-relative"
56
+ :class="{ 'pr-lg-0': constrained }"
57
+ cols="12"
58
+ lg="8">
59
+ <!-- h2 is first in DOM order for semantics; using order utility classes to rearrange -->
60
+ <h2
61
+ class="align-items-center d-flex font-size-300 justify-content-center justify-content-lg-start mb-150 mb-lg-100 order-1"
62
+ :class="{
63
+ 'pl-lg-100': constrained,
64
+ 'pl-lg-200': !constrained,
65
+ }">
66
+ <slot name="headline" />
67
+ </h2>
68
+ <div class="EsSupportCard-imageContainer mb-150 mb-lg-0 order-0">
69
+ <slot name="image">
70
+ <nuxt-img
71
+ v-if="imageAltText && imageSrc"
72
+ :alt="imageAltText"
73
+ class="bg-teal-200 rounded-circle"
74
+ height="100px"
75
+ :src="imageSrc"
76
+ width="100px" />
77
+ </slot>
78
+ </div>
79
+ <!--
80
+ we are giving 'EsSupportCard-description' a unique class (that we don't use directly in this
81
+ component) for consuming applications to use in targeting ::v-deep styles on <p> tags to remove
82
+ the natural <p> bottom margin, for example.
83
+
84
+ e.g. a CMS rich text component may generate a <p> tag within this that automatically
85
+ gets a bottom margin, which throws off the vertical centering. we are intentionally leaving
86
+ the removal of that bottom margin up to consuming applications rather than removing it on all
87
+ <p> tags within this element, in case they may want two <p> tags or any other markup in here.
88
+ -->
89
+ <div
90
+ class="EsSupportCard-description font-size-75 font-size-lg-100 mb-150 mb-lg-0 order-2"
91
+ :class="{
92
+ 'pl-lg-100': constrained,
93
+ 'pl-lg-200': !constrained,
94
+ }">
95
+ <slot name="description" />
96
+ </div>
97
+ </es-col>
98
+ <es-col
99
+ cols="12"
100
+ lg="4"
101
+ xxl="3">
102
+ <es-button
103
+ class="w-100"
104
+ :class="{ 'mb-100': showSecondaryCta }"
105
+ :href="primaryCtaUrl"
106
+ :target="primaryCtaTarget"
107
+ variant="primary">
108
+ {{ primaryCtaText }}
109
+ </es-button>
110
+ <es-button
111
+ v-if="showSecondaryCta"
112
+ class="w-100"
113
+ :href="secondaryCtaUrl"
114
+ outline
115
+ :target="secondaryCtaTarget"
116
+ variant="primary">
117
+ {{ secondaryCtaText }}
118
+ </es-button>
119
+ </es-col>
120
+ </es-row>
121
+ </es-card>
122
+ </template>
123
+
124
+ <style lang="scss">
125
+ @use '@energysage/es-ds-styles/scss/mixins/breakpoints' as breakpoints;
126
+
127
+ @include breakpoints.media-breakpoint-up(lg) {
128
+ .EsSupportCard {
129
+ &-contentColumn {
130
+ /* account for the size of the image */
131
+ min-height: 100px;
132
+ /* 15px standard column padding + 100px image width */
133
+ padding-left: 115px;
134
+ }
135
+
136
+ &-description {
137
+ /* limit line length on larger breakpoints */
138
+ max-width: 450px;
139
+ }
140
+
141
+ &-imageContainer {
142
+ /* account for 15px standard column padding */
143
+ left: 15px;
144
+ position: absolute;
145
+ top: 50%;
146
+ transform: translateY(-50%);
147
+ }
148
+ }
149
+ }
150
+ </style>
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ defineProps({
3
+ link: {
4
+ type: String,
5
+ required: true,
6
+ },
7
+ src: {
8
+ type: String,
9
+ required: true,
10
+ },
11
+ });
12
+
13
+ const slots = useSlots();
14
+ const hasTitle = computed(() => !!slots.title);
15
+ const hasLinkCopy = computed(() => !!slots.linkCopy);
16
+ </script>
17
+
18
+ <template>
19
+ <div class="d-flex support-holder align-items-center">
20
+ <div class="mr-50">
21
+ <a
22
+ target="_blank"
23
+ aria-label="Support"
24
+ :href="link">
25
+ <img
26
+ class="image"
27
+ :src="src"
28
+ alt="EnergySage support" />
29
+ </a>
30
+ </div>
31
+ <div class="d-flex flex-column">
32
+ <div>
33
+ <div class="h5 mb-25 font-size-base font-weight-bold">
34
+ <slot
35
+ v-if="hasTitle"
36
+ name="title" />
37
+ <template v-else> Need help signing up? </template>
38
+ </div>
39
+ </div>
40
+ <div>
41
+ <a
42
+ target="_blank"
43
+ class="font-size-75"
44
+ :href="link">
45
+ <slot
46
+ v-if="hasLinkCopy"
47
+ name="linkCopy" />
48
+ <template v-else> Schedule a free call with your EnergySage Advisor. </template>
49
+ </a>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </template>
54
+
55
+ <style lang="scss" scoped>
56
+ .support-holder {
57
+ max-width: 325px;
58
+
59
+ .image {
60
+ height: 64px;
61
+ width: 64px;
62
+ border-radius: 2rem;
63
+ object-fit: contain;
64
+ }
65
+ }
66
+ </style>
@@ -0,0 +1,7 @@
1
+ <!-- eslint-disable vue/valid-template-root -->
2
+ <template>
3
+ <!--
4
+ While this file needs to exist to keep Nuxt happy, it doesn't actually do anything.
5
+ Instances of es-tab are placed inside es-tabs, and es-tabs handles their display.
6
+ -->
7
+ </template>
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ import TabPanel from 'primevue/tabpanel';
3
+ import TabView from 'primevue/tabview';
4
+
5
+ // allow use of v-model on this component
6
+ const model = defineModel<number>();
7
+
8
+ // get the list of elements provided as children to the default slot
9
+ const panels = useSlots().default?.() || [];
10
+
11
+ // keep track of the active tab index
12
+ // - from above, if v-model is used
13
+ // - from below, if the tab component receives a click
14
+ const activeIndex: Ref<number> = ref(model.value || 0);
15
+
16
+ // watch the v-model for changes and update the active index if it does change
17
+ watch(model, (newVal) => {
18
+ // @ts-expect-error I don't know why
19
+ activeIndex.value = newVal;
20
+ });
21
+
22
+ // called from the TabView when the user clicks a tab to make it active
23
+ const updateActiveIndex = (index: number) => {
24
+ model.value = index;
25
+ };
26
+ </script>
27
+
28
+ <template>
29
+ <tab-view
30
+ :active-index="activeIndex"
31
+ :pt="{
32
+ root: 'tabs es-tabs',
33
+ nav: 'list-unstyled nav position-relative',
34
+ tab: 'nav-link',
35
+ inkbar: 'inkbar position-absolute',
36
+ panelContainer: 'tab-content',
37
+ }"
38
+ @update:active-index="updateActiveIndex">
39
+ <!-- @vue-expect-error -->
40
+ <tab-panel
41
+ v-for="(panel, index) in panels"
42
+ :key="index"
43
+ :header="panel.props.title"
44
+ :pt="{
45
+ header: 'nav-item',
46
+ headerAction: ({ context }) => ({
47
+ class: [
48
+ 'nav-link',
49
+ {
50
+ 'active active-tab font-weight-bolder': context.active,
51
+ },
52
+ ],
53
+ }),
54
+ content: ({ context }) => ({
55
+ class: [
56
+ 'tab-pane mt-100',
57
+ {
58
+ active: context.active,
59
+ },
60
+ ],
61
+ }),
62
+ }">
63
+ <!-- @vue-expect-error -->
64
+ <component
65
+ :is="item"
66
+ v-for="(item, idx) in panel.children.default()"
67
+ :key="idx" />
68
+ </tab-panel>
69
+ </tab-view>
70
+ </template>
@@ -0,0 +1,94 @@
1
+ <script setup lang="ts">
2
+ import InputOtp from 'primevue/inputotp';
3
+
4
+ const model = defineModel<Array<string>>();
5
+ const verificationCode = ref<string>();
6
+
7
+ const props = defineProps({
8
+ charCount: {
9
+ type: Number,
10
+ default: 5,
11
+ },
12
+ disabled: {
13
+ type: Boolean,
14
+ default: false,
15
+ },
16
+ });
17
+
18
+ const count = ref(props.charCount);
19
+ watch(model, (newVal) => {
20
+ verificationCode.value = newVal ? newVal.join('') : '';
21
+ });
22
+
23
+ // Note: @update:model-value is erroneously requiring a boolean function argument,
24
+ // workaround for error is to specify any
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ const updateCode = (newValue: any) => {
27
+ model.value = newValue.split('');
28
+ };
29
+ </script>
30
+
31
+ <template>
32
+ <div>
33
+ <input-otp
34
+ v-model="verificationCode"
35
+ class="custom-otp"
36
+ v-bind="$attrs"
37
+ :length="count"
38
+ integer-only
39
+ @update:model-value="updateCode">
40
+ <template #default="{ attrs, events, index }">
41
+ <input
42
+ :id="index.toString()"
43
+ type="text"
44
+ class="custom-otp-input form-control"
45
+ :disabled="props.disabled"
46
+ v-bind="attrs"
47
+ v-on="events" />
48
+ </template>
49
+ </input-otp>
50
+ </div>
51
+ </template>
52
+
53
+ <style lang="scss" scoped>
54
+ @use '@energysage/es-ds-styles/scss/variables' as variables;
55
+ @use '@energysage/es-ds-styles/scss/mixins/breakpoints' as breakpoints;
56
+
57
+ .custom-otp {
58
+ display: flex;
59
+ gap: 10px;
60
+ }
61
+ .custom-otp-input {
62
+ font-weight: 600 !important;
63
+ font-size: 1.875rem !important;
64
+ line-height: 2.25rem !important;
65
+ text-align: center;
66
+ height: 5rem;
67
+ width: 5rem;
68
+ appearance: textfield;
69
+ border: variables.$verification-code-border;
70
+ border-radius: variables.$verification-code-border-radius !important;
71
+ box-shadow: variables.$verification-code-shadow;
72
+ height: variables.$verification-code-dimension-lg;
73
+ width: variables.$verification-code-dimension-lg;
74
+ flex: 0 0 auto;
75
+
76
+ &:focus {
77
+ border-color: variables.$verification-code-focus-border-color;
78
+ }
79
+
80
+ @include breakpoints.media-breakpoint-down(md) {
81
+ height: variables.$verification-code-dimension-xs;
82
+ width: variables.$verification-code-dimension-xs;
83
+ }
84
+
85
+ &::-webkit-outer-spin-button,
86
+ &::-webkit-inner-spin-button {
87
+ appearance: none;
88
+ }
89
+
90
+ &:last-child {
91
+ margin-right: 0 !important;
92
+ }
93
+ }
94
+ </style>
@@ -0,0 +1,60 @@
1
+ <script setup lang="ts">
2
+ // Props
3
+ interface IProps {
4
+ altText: string;
5
+ coverImageUrl?: string;
6
+ embedUrl: string;
7
+ }
8
+ const props = withDefaults(defineProps<IProps>(), {
9
+ coverImageUrl: '',
10
+ });
11
+ const showVideo = ref(false);
12
+
13
+ // Methods
14
+ function getVideoIdFromUrl(url: string) {
15
+ const embedPattern = /https:\/\/www\.youtube\.com\/embed\/(?<videoId>[A-Za-z0-9-]+)/;
16
+ const parsedUrl = embedPattern.exec(url);
17
+ if (parsedUrl) {
18
+ return parsedUrl?.groups?.videoId;
19
+ }
20
+ return '';
21
+ }
22
+
23
+ // Computed
24
+ const videoId = computed(() => getVideoIdFromUrl(props.embedUrl));
25
+ const embedUrlWithParams = computed(
26
+ () => `https://www.youtube.com/embed/${videoId.value}?rel=0&autoplay=1&cc_load_policy=1&cc_lang_pref=en`,
27
+ );
28
+ </script>
29
+
30
+ <template>
31
+ <div>
32
+ <div
33
+ v-if="showVideo"
34
+ class="embed-responsive embed-responsive-16by9">
35
+ <iframe
36
+ :id="`youtube-video-${videoId}`"
37
+ :src="embedUrlWithParams"
38
+ class="embed-responsive-item"
39
+ frameborder="0"></iframe>
40
+ </div>
41
+ <es-card
42
+ v-else
43
+ :id="`youtube-placeholder-${videoId}`"
44
+ class="EsVideo-button bg-transparent overflow-hidden p-0 position-relative w-100"
45
+ variant="interactive"
46
+ @click="showVideo = true">
47
+ <slot v-if="$slots.default" />
48
+ <nuxt-img
49
+ v-else
50
+ class="EsVideo-image d-block w-100"
51
+ sizes="md:530px sm:275px"
52
+ :src="props.coverImageUrl"
53
+ :alt="props.altText" />
54
+ <icon-video-play
55
+ class="EsVideo-icon position-absolute abs-center"
56
+ width="74px"
57
+ height="54px" />
58
+ </es-card>
59
+ </div>
60
+ </template>
@@ -0,0 +1,64 @@
1
+ <script setup lang="ts">
2
+ // @ts-expect-error module has no types
3
+ import truncate from 'html-truncate';
4
+
5
+ const props = defineProps({
6
+ /**
7
+ * Full text content
8
+ */
9
+ content: {
10
+ type: String,
11
+ required: true,
12
+ },
13
+ /**
14
+ * Maximum character length of text before it is truncated
15
+ */
16
+ length: {
17
+ type: Number,
18
+ required: true,
19
+ },
20
+ /**
21
+ * Text for button to show more text
22
+ */
23
+ clamp: {
24
+ type: String,
25
+ required: false,
26
+ default: 'Show more',
27
+ },
28
+ /**
29
+ * Text for button to hide text
30
+ */
31
+ less: {
32
+ type: String,
33
+ required: false,
34
+ default: 'Show less',
35
+ },
36
+ });
37
+
38
+ const isClamped = ref(true);
39
+
40
+ const truncatedContent = computed(() => truncate(props.content, props.length));
41
+ const isTruncated = computed(() => truncatedContent.value !== props.content);
42
+ const buttonText = computed(() => (isClamped.value ? props.clamp : props.less));
43
+ const bodyContent = computed(() => {
44
+ if (isClamped.value) {
45
+ return truncatedContent.value;
46
+ }
47
+ return props.content;
48
+ });
49
+ </script>
50
+
51
+ <template>
52
+ <div>
53
+ <!-- eslint-disable-next-line vue/no-v-html -->
54
+ <span v-html="bodyContent" />
55
+ <es-button
56
+ v-if="isTruncated"
57
+ inline
58
+ variant="link"
59
+ class="ml-50 p-0"
60
+ @click="isClamped = !isClamped">
61
+ {{ buttonText }}
62
+ </es-button>
63
+ </div>
64
+ </template>