@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,163 @@
1
+ <script lang="ts">
2
+ import IconFacebook from './icon/facebook.vue';
3
+ import IconInstagram from './icon/instagram.vue';
4
+ import IconLinkedin from './icon/linkedin.vue';
5
+ import IconTwitter from './icon/twitter.vue';
6
+ import IconYoutube from './icon/youtube.vue';
7
+
8
+ export default {
9
+ name: 'EsFooter',
10
+ components: {
11
+ IconFacebook,
12
+ IconInstagram,
13
+ IconLinkedin,
14
+ IconTwitter,
15
+ IconYoutube,
16
+ },
17
+ props: {
18
+ content: {
19
+ type: Object,
20
+ required: true,
21
+ },
22
+ },
23
+ computed: {
24
+ copyrightText() {
25
+ return this.content.copyrightText.replace('{currentYear}', new Date().getFullYear());
26
+ },
27
+ },
28
+ };
29
+ </script>
30
+
31
+ <template>
32
+ <div class="es-footer clearfix">
33
+ <!-- Make an impact -->
34
+ <div class="bg-blue-900 text-white py-200 py-lg-300 px-200">
35
+ <div class="container px-0">
36
+ <div class="row">
37
+ <div
38
+ class="col col-12 col-md-5 col-lg-4 font-size-200 font-size-md-400 font-weight-bold mb-150 mb-md-0">
39
+ {{ content.banner.headline }}
40
+ <br />
41
+ {{ content.banner.subHeadline }}
42
+ </div>
43
+ <div class="col col-12 col-md-7 col-lg-8">
44
+ {{ content.banner.body }}
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ <!-- Make an impact -->
50
+ <div class="bg-blue-800 py-200 pt-lg-200 pb-lg-150 px-200">
51
+ <div class="container px-0">
52
+ <div class="row">
53
+ <!-- Link columns -->
54
+ <div class="col col-12 col-lg-8 order-lg-1 text-white">
55
+ <div class="row">
56
+ <div
57
+ v-for="column in content.columns"
58
+ :key="column.title"
59
+ class="col col-6 col-md-3 mb-100 mb-lg-0">
60
+ <ul class="list-unstyled mb-lg-0">
61
+ <li class="font-weight-bold font-size-75 font-size-md-100 mb-100 mb-lg-50">
62
+ {{ column.title }}
63
+ </li>
64
+ <li
65
+ v-for="link in column.links"
66
+ :key="link.text"
67
+ class="mt-0 mt-lg-25 mb-100 mb-lg-50 font-size-75">
68
+ <es-nav-bar-link
69
+ :href="link.url"
70
+ :target="link.newTab ? '_blank' : null"
71
+ class="text-reset">
72
+ {{ link.text }}
73
+ </es-nav-bar-link>
74
+ </li>
75
+ </ul>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ <!-- Link columns -->
80
+ <!-- Logo and Social-->
81
+ <div class="col col-12 col-lg-4 order-lg-0">
82
+ <es-nav-bar-link
83
+ class="d-block mb-150 d-flex justify-content-center justify-content-lg-start"
84
+ :href="content.home.link">
85
+ <span class="sr-only">
86
+ {{ content.home.name }}
87
+ </span>
88
+ <div class="es-footer-logo">
89
+ <slot name="logo" />
90
+ </div>
91
+ </es-nav-bar-link>
92
+ <!-- Social icons -->
93
+ <div class="d-flex text-white justify-content-center justify-content-lg-start">
94
+ <es-nav-bar-link
95
+ v-for="(iconLink, index) in content.socialLinks"
96
+ :key="iconLink.text"
97
+ class="text-reset"
98
+ :class="{ 'mr-150': index < content.socialLinks.length - 1 }"
99
+ :href="iconLink.url"
100
+ target="_blank">
101
+ <span class="sr-only">{{ iconLink.text }}</span>
102
+ <component :is="iconLink.icon" />
103
+ </es-nav-bar-link>
104
+ </div>
105
+ <!-- Social icons -->
106
+ </div>
107
+ <!-- Logo and Social -->
108
+ </div>
109
+ </div>
110
+ </div>
111
+ <div class="bg-blue-700 text-blue-50 px-200 py-100">
112
+ <div class="container px-0">
113
+ <!-- Trademark Info -->
114
+ <p class="font-size-75">
115
+ {{ content.trademarkText }}
116
+ </p>
117
+ <p class="mb-200 font-size-75">
118
+ {{ copyrightText }}
119
+ </p>
120
+ <!-- Trademark Info -->
121
+ <!-- Legal -->
122
+ <hr class="border-top border-blue-500 m-0" />
123
+ <div class="row justify-content-center pt-100">
124
+ <div
125
+ v-for="link in content.legalLinks"
126
+ :key="link.text"
127
+ class="col col-6 col-lg-auto font-size-75 font-weight-semibold font-size-md-50 font-weight-md-normal mt-25 mb-50 px-lg-300">
128
+ <es-nav-bar-link
129
+ :href="link.url"
130
+ class="text-reset font-weight-normal">
131
+ {{ link.text }}
132
+ </es-nav-bar-link>
133
+ </div>
134
+ </div>
135
+ <!-- Legal -->
136
+ </div>
137
+ </div>
138
+ <div class="bg-blue-50 px-200 py-100">
139
+ <div class="container px-0">
140
+ <!-- DOE -->
141
+ <div class="d-lg-flex align-items-end">
142
+ <!-- eslint-disable-next-line -->
143
+ <img
144
+ :src="content.departmentOfEnergy.logoUrl"
145
+ alt="department of energy logo"
146
+ aria-hidden="true"
147
+ class="mr-100 mb-100 mb-lg-0"
148
+ width="99"
149
+ height="24" />
150
+ <p class="mb-0 font-size-75 font-weight-normal font-weight-lg-semibold">
151
+ <es-nav-bar-link
152
+ :href="content.departmentOfEnergy.learnMore.link"
153
+ target="_blank"
154
+ class="text-blue-900">
155
+ {{ content.departmentOfEnergy.learnMore.text }}
156
+ </es-nav-bar-link>
157
+ </p>
158
+ </div>
159
+ <!-- DOE -->
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </template>
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ const model = defineModel<boolean>({
3
+ default: false,
4
+ });
5
+ defineOptions({
6
+ inheritAttrs: false,
7
+ });
8
+ defineProps({
9
+ id: {
10
+ type: String,
11
+ required: true,
12
+ },
13
+ disabled: {
14
+ type: Boolean,
15
+ default: false,
16
+ },
17
+ indeterminate: {
18
+ type: Boolean,
19
+ default: false,
20
+ },
21
+ });
22
+ </script>
23
+
24
+ <template>
25
+ <div class="custom-control custom-checkbox">
26
+ <input
27
+ v-bind="$attrs"
28
+ :id="id"
29
+ v-model="model"
30
+ type="checkbox"
31
+ class="custom-control-input"
32
+ :indeterminate="indeterminate"
33
+ :disabled="disabled" />
34
+ <label
35
+ :for="id"
36
+ class="custom-control-label">
37
+ <slot />
38
+ </label>
39
+ </div>
40
+ </template>
@@ -0,0 +1,130 @@
1
+ <script setup lang="ts">
2
+ import InputText from 'primevue/inputtext';
3
+ import InputMask from 'primevue/inputmask';
4
+ // Prevents attributes from being applied to first <div>
5
+ // v-bind="$attr" is on the input instead
6
+ defineOptions({
7
+ inheritAttrs: false,
8
+ });
9
+
10
+ defineProps({
11
+ /**
12
+ * Required
13
+ */
14
+ required: {
15
+ type: Boolean,
16
+ default: false,
17
+ },
18
+ /**
19
+ * Disabled
20
+ */
21
+ disabled: {
22
+ type: Boolean,
23
+ default: false,
24
+ },
25
+ /**
26
+ * Type
27
+ */
28
+ type: {
29
+ type: String,
30
+ default: 'text',
31
+ validator: (val: string) => ['text', 'email', 'number', 'password', 'tel', 'maskedTel'].includes(val),
32
+ },
33
+ /**
34
+ * ID
35
+ * required
36
+ */
37
+ id: {
38
+ type: String,
39
+ required: true,
40
+ },
41
+ /**
42
+ * state
43
+ */
44
+ state: {
45
+ type: [Boolean, null],
46
+ default: null,
47
+ },
48
+ labelSrOnly: {
49
+ type: Boolean,
50
+ default: false,
51
+ },
52
+ phoneMaskValue: {
53
+ type: String,
54
+ default: '(999) 999-9999',
55
+ required: false,
56
+ },
57
+ });
58
+
59
+ const slots = useSlots();
60
+
61
+ const hasSuccess = () => !!slots.successMessage;
62
+ const hasMessage = () => !!slots.message;
63
+ const hasError = () => !!slots.errorMessage;
64
+ const hasExtraContext = () => !!slots.extraContext;
65
+ </script>
66
+
67
+ <template>
68
+ <div
69
+ class="input-wrapper justify-content-end"
70
+ :class="$attrs.class"
71
+ :required="required">
72
+ <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->
73
+ <label
74
+ :for="id"
75
+ class="label justify-content-start"
76
+ :class="{ 'sr-only': labelSrOnly }">
77
+ <slot name="label" />
78
+ <span
79
+ v-if="required"
80
+ class="text-danger">
81
+ *
82
+ </span>
83
+ </label>
84
+ <p
85
+ v-if="hasExtraContext()"
86
+ class="mb-25 font-size-75">
87
+ <slot name="extraContext" />
88
+ </p>
89
+ <div class="input-holder position-relative">
90
+ <!-- prefix icons are decorative only so are aria-hidden -->
91
+ <span
92
+ v-if="$slots.prefixIcon"
93
+ aria-hidden="true"
94
+ class="prefix-icon position-absolute">
95
+ <slot name="prefixIcon" />
96
+ </span>
97
+ <component
98
+ :is="type === 'maskedTel' ? InputMask : InputText"
99
+ :id="id"
100
+ v-bind="{ ...$attrs, class: undefined }"
101
+ :type="type"
102
+ class="es-form-input form-control w-100"
103
+ :class="{
104
+ 'has-prefix-icon': $slots.prefixIcon,
105
+ 'is-invalid': state === false,
106
+ }"
107
+ :mask="phoneMaskValue"
108
+ :disabled="disabled"
109
+ :invalid="state === false" />
110
+ <small
111
+ v-if="hasMessage() && ((!hasSuccess() && state) || state == null)"
112
+ class="text-muted">
113
+ <slot name="message" />
114
+ </small>
115
+ <small
116
+ v-if="state === false && (hasError() || required)"
117
+ class="text-danger">
118
+ <slot
119
+ v-if="hasError()"
120
+ name="errorMessage" />
121
+ <template v-else-if="required"> This field is required. </template>
122
+ </small>
123
+ <small
124
+ v-if="state && hasSuccess()"
125
+ class="text-success">
126
+ <slot name="successMessage" />
127
+ </small>
128
+ </div>
129
+ </div>
130
+ </template>
@@ -0,0 +1,81 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps({
3
+ show: {
4
+ type: Boolean,
5
+ default: false,
6
+ },
7
+ timeout: {
8
+ type: Number,
9
+ default: 20,
10
+ required: false,
11
+ },
12
+ variant: {
13
+ type: String,
14
+ default: 'danger',
15
+ required: false,
16
+ validator: (val: string) => ['danger', 'success', 'primary'].includes(val),
17
+ },
18
+ });
19
+
20
+ const emit = defineEmits(['hidden']);
21
+
22
+ const hide = () => {
23
+ emit('hidden');
24
+ };
25
+
26
+ watch(
27
+ () => props.show,
28
+ (newValue, oldValue) => {
29
+ if (newValue && !oldValue) {
30
+ setTimeout(() => {
31
+ hide();
32
+ }, props.timeout * 1000);
33
+ }
34
+ },
35
+ );
36
+ </script>
37
+
38
+ <template>
39
+ <transition>
40
+ <div
41
+ v-if="show"
42
+ role="alert"
43
+ aria-live="polite"
44
+ aria-atomic="true"
45
+ class="alert es-form-msg my-100 alert-dismissible"
46
+ :class="`alert-${variant}`">
47
+ <div class="d-flex pr-100">
48
+ <div class="icon-wrapper flex-shrink-0 mr-100">
49
+ <icon-circle-alert v-if="variant === 'danger'" />
50
+ <icon-circle-check v-if="variant === 'success'" />
51
+ <icon-info v-if="variant === 'primary'" />
52
+ </div>
53
+ <slot />
54
+ </div>
55
+ <button
56
+ type="button"
57
+ aria-label="Close"
58
+ class="close"
59
+ @click="hide">
60
+ <icon-x />
61
+ </button>
62
+ </div>
63
+ </transition>
64
+ </template>
65
+
66
+ <style lang="scss" scoped>
67
+ .form-msg {
68
+ flex: 0 0 100%;
69
+ padding-right: 2.5rem;
70
+ }
71
+
72
+ .v-enter-active,
73
+ .v-leave-active {
74
+ transition: opacity 0.15s;
75
+ }
76
+
77
+ .v-enter-from,
78
+ .v-leave-to {
79
+ opacity: 0;
80
+ }
81
+ </style>
@@ -0,0 +1,57 @@
1
+ <script setup lang="ts">
2
+ import { useFocus } from '@vueuse/core';
3
+
4
+ const inputElem = useTemplateRef('input-elem');
5
+ const { focused: inputFocused } = useFocus(inputElem);
6
+
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ interface IProps {
9
+ id: string;
10
+ name: string;
11
+ value: any;
12
+ disabled?: boolean;
13
+ inline?: boolean;
14
+ displayName?: string;
15
+ }
16
+
17
+ const props = withDefaults(defineProps<IProps>(), {
18
+ disabled: false,
19
+ inline: false,
20
+ displayName: '',
21
+ });
22
+
23
+ // Need to define the implicit emit from v-model, so that it can also get fired
24
+ // from the label clicks
25
+ const emit = defineEmits(['update:modelValue']);
26
+
27
+ function handleRadioButtonClick() {
28
+ if (!props.disabled) {
29
+ emit('update:modelValue', props.value);
30
+ }
31
+ }
32
+
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ const model = defineModel<any>();
35
+ const isChecked = computed(() => props.value === model.value);
36
+ </script>
37
+
38
+ <template>
39
+ <label
40
+ class="es-form-radio-card es-card interactive w-100 btn btn-outline-primary"
41
+ :class="{ active: isChecked, disabled: props.disabled, focus: isChecked || inputFocused }"
42
+ @click="handleRadioButtonClick">
43
+ <input
44
+ :id="id"
45
+ ref="input-elem"
46
+ v-model="model"
47
+ :disabled="props.disabled"
48
+ type="radio"
49
+ name="props.name"
50
+ :value="props.value"
51
+ :checked="isChecked"
52
+ @click="handleRadioButtonClick" />
53
+ <slot>
54
+ {{ displayName }}
55
+ </slot>
56
+ </label>
57
+ </template>
@@ -0,0 +1,78 @@
1
+ <script setup lang="ts">
2
+ interface Option {
3
+ id: string;
4
+ text: string;
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ value: any;
7
+ disabled?: boolean;
8
+ inline?: boolean;
9
+ }
10
+
11
+ interface Props {
12
+ id: string;
13
+ label: string;
14
+ name?: string;
15
+ options?: Option[];
16
+ inline?: boolean;
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ modelValue?: any;
19
+ hasIcon?: boolean;
20
+ labelClass?: string;
21
+ labelSrOnly?: boolean;
22
+ }
23
+ const props = withDefaults(defineProps<Props>(), {
24
+ name: '',
25
+ options: undefined,
26
+ inline: false,
27
+ modelValue: undefined,
28
+ hasIcon: false,
29
+ labelClass: 'font-size-h3',
30
+ labelSrOnly: false,
31
+ });
32
+
33
+ const emit = defineEmits(['update:model-value']);
34
+
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ function handleUpdate(value: any) {
37
+ emit('update:model-value', value);
38
+ }
39
+ </script>
40
+
41
+ <template>
42
+ <fieldset
43
+ :id="`${props.id}-fieldset`"
44
+ class="form-group">
45
+ <legend
46
+ :id="`${props.id}-legend`"
47
+ class="font-size-h1 font-weight-bolder mb-200 pb-0 text-dark"
48
+ :class="{ 'sr-only': props.labelSrOnly }"
49
+ tabindex="-1">
50
+ {{ props.label }}
51
+ </legend>
52
+ <div>
53
+ <div
54
+ :id="`${props.id}-radiogroup`"
55
+ role="radiogroup"
56
+ tabindex="-1"
57
+ class="es-form-radio-cards d-flex justify-content-center btn-group-vertical"
58
+ :class="{ 'has-icon': props.hasIcon }">
59
+ <slot :options="options">
60
+ <es-form-radio-card
61
+ v-for="option in options"
62
+ :id="option.id"
63
+ :key="option.value"
64
+ :disabled="option?.disabled || false"
65
+ :name="props.name"
66
+ :value="option.value"
67
+ :model-value="props.modelValue"
68
+ :inline="props.inline || false"
69
+ @update:model-value="handleUpdate">
70
+ <span :class="props.labelClass">
71
+ {{ option.text }}
72
+ </span>
73
+ </es-form-radio-card>
74
+ </slot>
75
+ </div>
76
+ </div>
77
+ </fieldset>
78
+ </template>
@@ -0,0 +1,124 @@
1
+ <script setup lang="ts">
2
+ // Prevents attributes from being applied to first <div>
3
+ // v-bind="$attr" is on the input instead
4
+ defineOptions({
5
+ inheritAttrs: false,
6
+ });
7
+
8
+ defineProps({
9
+ /**
10
+ * Required
11
+ */
12
+ required: {
13
+ type: Boolean,
14
+ default: false,
15
+ },
16
+ /**
17
+ * Disabled
18
+ */
19
+ disabled: {
20
+ type: Boolean,
21
+ default: false,
22
+ },
23
+ /**
24
+ * ID
25
+ * required
26
+ */
27
+ id: {
28
+ type: String,
29
+ required: true,
30
+ },
31
+ /**
32
+ * state
33
+ */
34
+ state: {
35
+ type: [Boolean, null],
36
+ default: null,
37
+ },
38
+ });
39
+ const model = defineModel<string>();
40
+
41
+ const slots = useSlots();
42
+
43
+ const hasSuccess = () => !!slots.successMessage;
44
+ const hasMessage = () => !!slots.message;
45
+ const hasError = () => !!slots.errorMessage;
46
+ </script>
47
+
48
+ <template>
49
+ <div
50
+ class="input-wrapper justify-content-end"
51
+ :required="required">
52
+ <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->
53
+ <label
54
+ :for="id"
55
+ class="label justify-content-start">
56
+ <slot name="label" />
57
+
58
+ <span
59
+ v-if="required"
60
+ class="text-danger">
61
+ *
62
+ </span>
63
+ </label>
64
+
65
+ <div class="input-holder">
66
+ <textarea
67
+ :id="id"
68
+ v-model="model"
69
+ v-bind="$attrs"
70
+ class="es-form-textarea w-100 form-control"
71
+ :class="{ 'is-invalid': state === false }"
72
+ :disabled="disabled"
73
+ :invalid="state === false" />
74
+ <small
75
+ v-if="hasMessage() && ((!hasSuccess() && state) || state == null)"
76
+ class="text-muted">
77
+ <slot name="message" />
78
+ </small>
79
+ <small
80
+ v-if="state === false && (hasError() || required)"
81
+ class="text-danger">
82
+ <slot
83
+ v-if="hasError()"
84
+ name="errorMessage" />
85
+ <template v-else-if="required"> This field is required. </template>
86
+ </small>
87
+ <small
88
+ v-if="state && hasSuccess()"
89
+ class="text-success">
90
+ <slot name="successMessage" />
91
+ </small>
92
+ </div>
93
+ </div>
94
+ </template>
95
+
96
+ <style lang="scss" scoped>
97
+ @use '@energysage/es-ds-styles/scss/variables' as variables;
98
+
99
+ .es-form-textarea {
100
+ min-height: 8.125rem;
101
+ }
102
+
103
+ .es-form-textarea:disabled,
104
+ .es-form-textarea[readonly] {
105
+ border: 0;
106
+ }
107
+
108
+ .is-invalid {
109
+ color: variables.$danger;
110
+ }
111
+
112
+ .form-inline .input-wrapper {
113
+ display: flex;
114
+ flex: 0 0 100%;
115
+
116
+ label {
117
+ flex: 0 0 30%;
118
+ }
119
+
120
+ .input-holder {
121
+ flex: 0 0 70%;
122
+ }
123
+ }
124
+ </style>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps({
3
+ inline: {
4
+ type: Boolean,
5
+ default: false,
6
+ },
7
+ });
8
+ </script>
9
+
10
+ <template>
11
+ <form
12
+ :class="{ 'form-inline': props.inline }"
13
+ v-bind="$attrs">
14
+ <slot />
15
+ </form>
16
+ </template>