@kiva/kv-components 3.107.0 → 3.107.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/components/.storybook/main.js +85 -0
  3. package/dist/components/.storybook/package.json +3 -0
  4. package/dist/components/.storybook/preview.js +61 -0
  5. package/dist/components/.storybook/tailwind.css +5 -0
  6. package/dist/components/KvAccordionItem.vue +130 -0
  7. package/dist/components/KvActivityRow.vue +33 -0
  8. package/dist/components/KvBorrowerImage.vue +179 -0
  9. package/dist/components/KvButton.vue +287 -0
  10. package/dist/components/KvCarousel.vue +297 -0
  11. package/dist/components/KvCartModal.vue +365 -0
  12. package/dist/components/KvCheckbox.vue +203 -0
  13. package/dist/components/KvChip.vue +54 -0
  14. package/dist/components/KvClassicLoanCard.vue +527 -0
  15. package/dist/components/KvCommentsAdd.vue +135 -0
  16. package/dist/components/KvCommentsContainer.vue +84 -0
  17. package/dist/components/KvCommentsHeartButton.vue +70 -0
  18. package/dist/components/KvCommentsList.vue +68 -0
  19. package/dist/components/KvCommentsListItem.vue +241 -0
  20. package/dist/components/KvCommentsReplyButton.vue +52 -0
  21. package/dist/components/KvContentfulImg.vue +273 -0
  22. package/dist/components/KvCountdownTimer.vue +59 -0
  23. package/dist/components/KvExpandable.vue +84 -0
  24. package/dist/components/KvExpandableQuestion.vue +120 -0
  25. package/dist/components/KvFlag.vue +120 -0
  26. package/dist/components/KvGrid.vue +28 -0
  27. package/dist/components/KvImpactDashboardHeader.vue +40 -0
  28. package/dist/components/KvInlineActivityCard.vue +55 -0
  29. package/dist/components/KvInlineActivityFeed.vue +38 -0
  30. package/dist/components/KvIntroductionLoanCard.vue +446 -0
  31. package/dist/components/KvLendAmountButton.vue +65 -0
  32. package/dist/components/KvLendCta.vue +451 -0
  33. package/dist/components/KvLightbox.vue +334 -0
  34. package/dist/components/KvLineGraph.vue +128 -0
  35. package/dist/components/KvLoadingPlaceholder.vue +38 -0
  36. package/dist/components/KvLoadingSpinner.vue +81 -0
  37. package/dist/components/KvLoanActivities.vue +268 -0
  38. package/dist/components/KvLoanBookmark.vue +39 -0
  39. package/dist/components/KvLoanCallouts.vue +53 -0
  40. package/dist/components/KvLoanProgressGroup.vue +76 -0
  41. package/dist/components/KvLoanTag.vue +88 -0
  42. package/dist/components/KvLoanTeamPick.vue +44 -0
  43. package/dist/components/KvLoanUse.vue +92 -0
  44. package/dist/components/KvMap.vue +599 -0
  45. package/dist/components/KvMaterialIcon.vue +47 -0
  46. package/dist/components/KvPageContainer.vue +15 -0
  47. package/dist/components/KvPagination.vue +198 -0
  48. package/dist/components/KvPieChart.vue +257 -0
  49. package/dist/components/KvPopper.vue +178 -0
  50. package/dist/components/KvProgressBar.vue +149 -0
  51. package/dist/components/KvRadio.vue +198 -0
  52. package/dist/components/KvSelect.vue +114 -0
  53. package/dist/components/KvSideSheet.vue +134 -0
  54. package/dist/components/KvSwitch.vue +143 -0
  55. package/dist/components/KvTab.vue +90 -0
  56. package/dist/components/KvTabPanel.vue +64 -0
  57. package/dist/components/KvTabs.vue +182 -0
  58. package/dist/components/KvTextInput.vue +247 -0
  59. package/dist/components/KvTextLink.vue +138 -0
  60. package/dist/components/KvThemeProvider.vue +122 -0
  61. package/dist/components/KvToast.vue +221 -0
  62. package/dist/components/KvTooltip.vue +168 -0
  63. package/dist/components/KvTreeMapChart.vue +229 -0
  64. package/dist/components/KvUserAvatar.vue +132 -0
  65. package/dist/components/KvVerticalCarousel.vue +156 -0
  66. package/dist/components/KvVotingCard.vue +160 -0
  67. package/dist/components/KvVotingCardV2.vue +154 -0
  68. package/dist/components/KvWideLoanCard.vue +432 -0
  69. package/dist/components/stories/Forms.stories.js +62 -0
  70. package/dist/components/stories/KvAccordionItem.stories.js +24 -0
  71. package/dist/components/stories/KvActivityRow.stories.js +25 -0
  72. package/dist/components/stories/KvBorrowerImage.stories.js +68 -0
  73. package/dist/components/stories/KvButton.stories.js +144 -0
  74. package/dist/components/stories/KvCarousel.stories.js +426 -0
  75. package/dist/components/stories/KvCartModal.stories.js +54 -0
  76. package/dist/components/stories/KvCheckbox.stories.js +163 -0
  77. package/dist/components/stories/KvChip.stories.js +43 -0
  78. package/dist/components/stories/KvClassicLoanCard.stories.js +480 -0
  79. package/dist/components/stories/KvCommentsAdd.stories.js +32 -0
  80. package/dist/components/stories/KvCommentsContainer.stories.js +42 -0
  81. package/dist/components/stories/KvCommentsHeartButton.stories.js +25 -0
  82. package/dist/components/stories/KvCommentsList.stories.js +39 -0
  83. package/dist/components/stories/KvCommentsListItem.stories.js +45 -0
  84. package/dist/components/stories/KvCommentsReplyButton.stories.js +21 -0
  85. package/dist/components/stories/KvContentfulImg.stories.js +196 -0
  86. package/dist/components/stories/KvCountdownTimer.stories.js +30 -0
  87. package/dist/components/stories/KvExpandableQuestion.stories.js +129 -0
  88. package/dist/components/stories/KvFlag.stories.js +36 -0
  89. package/dist/components/stories/KvGrid.stories.js +97 -0
  90. package/dist/components/stories/KvImpactDashboardHeader.stories.js +22 -0
  91. package/dist/components/stories/KvInlineActivityCard.stories.js +69 -0
  92. package/dist/components/stories/KvInlineActivityFeed.stories.js +76 -0
  93. package/dist/components/stories/KvIntroductionLoanCard.stories.js +208 -0
  94. package/dist/components/stories/KvLendAmountButton.stories.js +31 -0
  95. package/dist/components/stories/KvLendCta.stories.js +177 -0
  96. package/dist/components/stories/KvLightbox.stories.js +304 -0
  97. package/dist/components/stories/KvLineGraph.stories.js +52 -0
  98. package/dist/components/stories/KvLoadingPlaceholder.stories.js +17 -0
  99. package/dist/components/stories/KvLoadingSpinner.stories.js +52 -0
  100. package/dist/components/stories/KvLoanActivities.stories.js +104 -0
  101. package/dist/components/stories/KvLoanBookmark.stories.js +22 -0
  102. package/dist/components/stories/KvLoanCallouts.stories.js +22 -0
  103. package/dist/components/stories/KvLoanProgressGroup.stories.js +29 -0
  104. package/dist/components/stories/KvLoanTag.stories.js +61 -0
  105. package/dist/components/stories/KvLoanTeamPick.stories.js +20 -0
  106. package/dist/components/stories/KvLoanUse.stories.js +60 -0
  107. package/dist/components/stories/KvMap.stories.js +121 -0
  108. package/dist/components/stories/KvMaterialIcon.stories.js +201 -0
  109. package/dist/components/stories/KvPageContainer.stories.js +50 -0
  110. package/dist/components/stories/KvPagination.stories.js +70 -0
  111. package/dist/components/stories/KvPieChart.stories.js +47 -0
  112. package/dist/components/stories/KvProgressBar.stories.js +53 -0
  113. package/dist/components/stories/KvRadio.stories.js +140 -0
  114. package/dist/components/stories/KvSelect.stories.js +125 -0
  115. package/dist/components/stories/KvSideSheet.stories.js +50 -0
  116. package/dist/components/stories/KvSwitch.stories.js +66 -0
  117. package/dist/components/stories/KvTabs.stories.js +106 -0
  118. package/dist/components/stories/KvTextInput.stories.js +194 -0
  119. package/dist/components/stories/KvTextLink.stories.js +55 -0
  120. package/dist/components/stories/KvThemeProvider.stories.js +178 -0
  121. package/dist/components/stories/KvToast.stories.js +117 -0
  122. package/dist/components/stories/KvTooltip.stories.js +26 -0
  123. package/dist/components/stories/KvTreeMapChart.stories.js +42 -0
  124. package/dist/components/stories/KvUserAvatar.stories.js +47 -0
  125. package/dist/components/stories/KvVerticalCarousel.stories.js +168 -0
  126. package/dist/components/stories/KvVotingCard.stories.js +33 -0
  127. package/dist/components/stories/KvVotingCardV2.stories.js +89 -0
  128. package/dist/components/stories/KvWideLoanCard.stories.js +292 -0
  129. package/dist/components/stories/StyleguidePrimitives.stories.js +499 -0
  130. package/dist/components/stories/StyleguideProse.stories.js +215 -0
  131. package/dist/data/countries-borders.json +1 -0
  132. package/dist/data/ne_110m_admin_0_countries.json +1 -0
  133. package/dist/utils/Alea.js +9 -0
  134. package/dist/utils/attrs.js +7 -0
  135. package/dist/utils/carousels.js +8 -0
  136. package/dist/{attrs.js → utils/chunk-3HK4G4NT.js} +1 -0
  137. package/dist/{loanCard.js → utils/chunk-55HF2ORX.js} +1 -0
  138. package/dist/{expander.js → utils/chunk-AY3PR5S4.js} +3 -2
  139. package/dist/{carousels.js → utils/chunk-AZPWOFD5.js} +1 -0
  140. package/dist/{printing.js → utils/chunk-B5J5WLAH.js} +1 -0
  141. package/dist/{Alea.js → utils/chunk-GPSH6OPA.js} +2 -1
  142. package/dist/{scrollLock.js → utils/chunk-HIY5IW65.js} +2 -1
  143. package/dist/{treemap.js → utils/chunk-MSMZIN54.js} +1 -0
  144. package/dist/{imageUtils.js → utils/chunk-OXJCCNNW.js} +1 -0
  145. package/dist/{touchEvents.js → utils/chunk-S3MABILA.js} +3 -2
  146. package/dist/{mapUtils.js → utils/chunk-VIGEMAKO.js} +5 -4
  147. package/dist/utils/chunk-YCNMJ4YV.js +37 -0
  148. package/dist/{loanUtils.js → utils/chunk-YFEC5ODJ.js} +7 -6
  149. package/dist/utils/expander.js +9 -0
  150. package/dist/utils/imageUtils.js +9 -0
  151. package/dist/utils/index.cjs +1118 -0
  152. package/dist/utils/index.js +166 -0
  153. package/dist/utils/loanCard.js +9 -0
  154. package/dist/utils/loanUtils.js +23 -0
  155. package/dist/utils/mapUtils.js +15 -0
  156. package/dist/utils/printing.js +9 -0
  157. package/dist/utils/scrollLock.js +13 -0
  158. package/dist/{throttle.js → utils/throttle.js} +1 -0
  159. package/dist/utils/touchEvents.js +11 -0
  160. package/dist/utils/treemap.js +7 -0
  161. package/package.json +7 -7
  162. package/utils/index.js +14 -0
  163. package/index.js +0 -3
  164. /package/dist/{Alea.cjs → utils/Alea.cjs} +0 -0
  165. /package/dist/{attrs.cjs → utils/attrs.cjs} +0 -0
  166. /package/dist/{carousels.cjs → utils/carousels.cjs} +0 -0
  167. /package/dist/{chunk-HV3AUBFT.js → utils/chunk-HV3AUBFT.js} +0 -0
  168. /package/dist/{expander.cjs → utils/expander.cjs} +0 -0
  169. /package/dist/{imageUtils.cjs → utils/imageUtils.cjs} +0 -0
  170. /package/dist/{loanCard.cjs → utils/loanCard.cjs} +0 -0
  171. /package/dist/{loanUtils.cjs → utils/loanUtils.cjs} +0 -0
  172. /package/dist/{mapUtils.cjs → utils/mapUtils.cjs} +0 -0
  173. /package/dist/{printing.cjs → utils/printing.cjs} +0 -0
  174. /package/dist/{scrollLock.cjs → utils/scrollLock.cjs} +0 -0
  175. /package/dist/{throttle.cjs → utils/throttle.cjs} +0 -0
  176. /package/dist/{touchEvents.cjs → utils/touchEvents.cjs} +0 -0
  177. /package/dist/{treemap.cjs → utils/treemap.cjs} +0 -0
@@ -0,0 +1,527 @@
1
+ <template>
2
+ <div
3
+ class="tw-flex tw-flex-col tw-bg-white tw-rounded tw-w-full tw-pb-1"
4
+ :class="{ 'tw-p-1': !largeCard, 'tw-pointer-events-none' : isLoading }"
5
+ data-testid="loan-card"
6
+ style="box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);"
7
+ :style="{ minWidth: '230px', maxWidth: cardWidth }"
8
+ >
9
+ <div class="tw-grow">
10
+ <div class="loan-card-active-hover">
11
+ <!-- Borrower image -->
12
+ <kv-loading-placeholder
13
+ v-if="isLoading"
14
+ class="tw-mb-1 tw-w-full"
15
+ :class="{ 'tw-rounded-t tw-rounded-b-none': largeCard, 'tw-rounded': !largeCard }"
16
+ :style="{ height: '15rem' }"
17
+ />
18
+ <div
19
+ v-else
20
+ class="tw-relative"
21
+ >
22
+ <component
23
+ :is="tag"
24
+ :to="readMorePath"
25
+ :href="readMorePath"
26
+ class="tw-flex"
27
+ aria-label="Borrower image"
28
+ @click.native="clickReadMore('Photo', $event)"
29
+ >
30
+ <kv-borrower-image
31
+ class="
32
+ tw-relative
33
+ tw-w-full
34
+ tw-bg-black
35
+ "
36
+ :class="{ 'tw-rounded-t': largeCard, 'tw-rounded': !largeCard }"
37
+ :alt="`Photo of ${borrowerName}`"
38
+ :aspect-ratio="imageAspectRatio"
39
+ :default-image="{ width: imageDefaultWidth }"
40
+ :hash="imageHash"
41
+ :images="imageSizes"
42
+ :photo-path="photoPath"
43
+ />
44
+
45
+ <div v-if="countryName">
46
+ <p
47
+ class="
48
+ tw-absolute
49
+ tw-bottom-1
50
+ tw-left-1
51
+ tw-text-primary
52
+ tw-bg-white
53
+ tw-rounded
54
+ tw-p-1
55
+ tw-mb-0
56
+ tw-mr-2
57
+ tw-text-h4
58
+ tw-inline-flex
59
+ tw-items-center"
60
+ style="padding: 2px 6px; text-transform: capitalize;"
61
+ >
62
+ <kv-material-icon
63
+ class="tw-h-2 tw-w-2"
64
+ :icon="mdiMapMarker"
65
+ />
66
+ {{ formattedLocation }}
67
+ </p>
68
+ </div>
69
+ </component>
70
+ <kv-loan-team-pick
71
+ v-if="isTeamPick"
72
+ class="tw-absolute"
73
+ :class="{ 'tw-right-6': !isVisitor, 'tw-right-0': isVisitor }"
74
+ style="top: -6px;"
75
+ data-testid="loan-card-teampick"
76
+ />
77
+ <kv-loan-bookmark
78
+ v-if="!isVisitor"
79
+ :loan-id="loanId"
80
+ :is-bookmarked="isBookmarked"
81
+ class="tw-absolute tw-right-1"
82
+ style="top: -6px;"
83
+ data-testid="loan-card-bookmark"
84
+ @toggle-bookmark="$emit('toggle-bookmark')"
85
+ />
86
+ </div>
87
+
88
+ <!-- Loan tag -->
89
+ <component
90
+ :is="tag"
91
+ :to="readMorePath"
92
+ :href="readMorePath"
93
+ class="tw-flex hover:tw-no-underline focus:tw-no-underline"
94
+ :class="{ 'tw-px-1': largeCard }"
95
+ aria-label="Loan tag"
96
+ @click.native="clickReadMore('Tag', $event)"
97
+ >
98
+ <kv-loan-tag
99
+ v-if="showTags && !isLoading"
100
+ :loan="loan"
101
+ />
102
+ </component>
103
+
104
+ <component
105
+ :is="tag"
106
+ :to="readMorePath"
107
+ :href="readMorePath"
108
+ class="loan-card-use tw-text-primary"
109
+ aria-label="Loan use"
110
+ @click.native="clickReadMore('Use', $event)"
111
+ >
112
+ <!-- Loan use -->
113
+ <div class="tw-mb-1.5 tw-pt-1">
114
+ <div
115
+ v-if="isLoading"
116
+ class="tw-w-full"
117
+ :class="{ 'tw-px-1': largeCard }"
118
+ style="height: 5.5rem;"
119
+ >
120
+ <div
121
+ v-for="(_n, i) in [...Array(4)]"
122
+ :key="i"
123
+ class="tw-h-2 tw-mb-1"
124
+ >
125
+ <kv-loading-placeholder />
126
+ </div>
127
+ </div>
128
+ <div v-else>
129
+ <kv-loan-use
130
+ :use="loanUse"
131
+ :loan-amount="loanAmount"
132
+ :status="loanStatus"
133
+ :borrower-count="loanBorrowerCount"
134
+ :name="borrowerName"
135
+ :distribution-model="distributionModel"
136
+ :class="{ 'tw-px-1': largeCard }"
137
+ />
138
+ </div>
139
+ </div>
140
+ </component>
141
+ </div>
142
+
143
+ <!-- Loan call outs -->
144
+ <kv-loading-placeholder
145
+ v-if="isLoading || typeof loanCallouts === 'undefined'"
146
+ class="tw-mt-1.5 tw-mb-1"
147
+ :class="{ 'tw-mx-1': largeCard }"
148
+ :style="{ width: '60%', height: '1.75rem', 'border-radius': '500rem' }"
149
+ />
150
+
151
+ <kv-loan-callouts
152
+ v-else
153
+ :callouts="loanCallouts"
154
+ class="tw-mt-1.5"
155
+ :class="{ 'tw-px-1': largeCard }"
156
+ @click="$emit('jump-filter-page', $event)"
157
+ />
158
+ </div>
159
+
160
+ <div
161
+ class="tw-flex tw-justify-between tw-mt-2"
162
+ :class="{ 'tw-px-1': largeCard }"
163
+ >
164
+ <!-- Fundraising -->
165
+ <div
166
+ v-if="!hasProgressData"
167
+ class="tw-w-full tw-pt-1 tw-pr-1"
168
+ >
169
+ <kv-loading-placeholder
170
+ class="tw-mb-0.5"
171
+ :style="{ width: '70%', height: '1.3rem' }"
172
+ />
173
+
174
+ <kv-loading-placeholder
175
+ class="tw-rounded"
176
+ :style="{ width: '70%', height: '0.5rem' }"
177
+ />
178
+ </div>
179
+
180
+ <div>
181
+ <component
182
+ :is="tag"
183
+ v-if="unreservedAmount > 0"
184
+ :to="readMorePath"
185
+ :href="readMorePath"
186
+ class="loan-card-progress tw-mt-1"
187
+ aria-label="Loan progress"
188
+ @click.native="clickReadMore('Progress', $event)"
189
+ >
190
+ <kv-loan-progress-group
191
+ id="loanProgress"
192
+ :money-left="`${unreservedAmount}`"
193
+ :progress-percent="fundraisingPercent"
194
+ class="tw-text-black"
195
+ />
196
+ </component>
197
+ </div>
198
+
199
+ <!-- CTA Button -->
200
+ <kv-loading-placeholder
201
+ v-if="!allDataLoaded"
202
+ class="tw-rounded tw-self-start"
203
+ :style="{ width: '9rem', height: '3rem' }"
204
+ />
205
+
206
+ <kv-lend-cta
207
+ v-else
208
+ :loan="loan"
209
+ :basket-items="basketItems"
210
+ :is-loading="isLoading"
211
+ :is-adding="isAdding"
212
+ :enable-five-dollars-notes="enableFiveDollarsNotes"
213
+ :five-dollars-selected="fiveDollarsSelected"
214
+ :kv-track-function="kvTrackFunction"
215
+ :show-view-loan="showViewLoan"
216
+ :custom-loan-details="customLoanDetails"
217
+ :external-links="externalLinks"
218
+ :route="route"
219
+ :user-balance="userBalance"
220
+ :get-cookie="getCookie"
221
+ :set-cookie="setCookie"
222
+ :enable-huge-amount="enableHugeAmount"
223
+ :is-visitor="isVisitor"
224
+ :primary-button-text="primaryButtonText"
225
+ :secondary-button-text="secondaryButtonText"
226
+ :secondary-button-handler="secondaryButtonHandler"
227
+ class="tw-mt-auto"
228
+ :class="{ 'tw-w-full' : unreservedAmount <= 0 }"
229
+ @add-to-basket="$emit('add-to-basket', $event)"
230
+ @show-loan-details="clickReadMore('ViewLoan', $event)"
231
+ @remove-from-basket="$emit('remove-from-basket', $event)"
232
+ />
233
+ </div>
234
+
235
+ <div
236
+ v-if="showContributors && lendersNumber && amountLent"
237
+ class="tw-text-center tw-w-full tw-mt-1 tw-font-medium "
238
+ >
239
+ <p>
240
+ {{ lendersNumber }} people contributed {{ amountLent }}
241
+ </p>
242
+ </div>
243
+
244
+ <div
245
+ v-if="combinedActivities.length > 0"
246
+ class="tw-pt-1.5"
247
+ >
248
+ <hr class="tw-border-tertiary tw-mb-1 tw-w-5/6 tw-mx-auto">
249
+ <KvLoanActivities
250
+ :loan="loan"
251
+ :combined-activities="combinedActivities"
252
+ :kv-track-function="kvTrackFunction"
253
+ :basket-items="basketItems"
254
+ :is-adding="isAdding"
255
+ :enable-five-dollars-notes="enableFiveDollarsNotes"
256
+ :five-dollars-selected="fiveDollarsSelected"
257
+ :show-view-loan="showViewLoan"
258
+ :custom-loan-details="customLoanDetails"
259
+ :external-links="externalLinks"
260
+ :route="route"
261
+ :user-balance="userBalance"
262
+ :get-cookie="getCookie"
263
+ :set-cookie="setCookie"
264
+ :error-msg="errorMsg"
265
+ :enable-huge-amount="enableHugeAmount"
266
+ :is-visitor="isVisitor"
267
+ @add-to-basket="$emit('add-to-basket', $event)"
268
+ />
269
+ </div>
270
+ </div>
271
+ </template>
272
+
273
+ <script>
274
+ import numeral from 'numeral';
275
+ import { loanCardComputedProperties, loanCardMethods } from '../utils/loanCard';
276
+
277
+ import KvLoanUse from './KvLoanUse.vue';
278
+ import KvBorrowerImage from './KvBorrowerImage.vue';
279
+ import KvLoanProgressGroup from './KvLoanProgressGroup.vue';
280
+ import KvLoanCallouts from './KvLoanCallouts.vue';
281
+ import KvLendCta from './KvLendCta.vue';
282
+ import KvLoanBookmark from './KvLoanBookmark.vue';
283
+ import KvLoanTag from './KvLoanTag.vue';
284
+ import KvMaterialIcon from './KvMaterialIcon.vue';
285
+ import KvLoadingPlaceholder from './KvLoadingPlaceholder.vue';
286
+ import KvLoanTeamPick from './KvLoanTeamPick.vue';
287
+ import KvLoanActivities from './KvLoanActivities.vue';
288
+
289
+ export default {
290
+ name: 'KvClassicLoanCard',
291
+ components: {
292
+ KvBorrowerImage,
293
+ KvLoadingPlaceholder,
294
+ KvLoanUse,
295
+ KvLoanProgressGroup,
296
+ KvMaterialIcon,
297
+ KvLendCta,
298
+ KvLoanTag,
299
+ KvLoanCallouts,
300
+ KvLoanBookmark,
301
+ KvLoanTeamPick,
302
+ KvLoanActivities,
303
+ },
304
+ props: {
305
+ loanId: {
306
+ type: Number,
307
+ default: undefined,
308
+ },
309
+ loan: {
310
+ type: Object,
311
+ default: null,
312
+ },
313
+ customLoanDetails: {
314
+ type: Boolean,
315
+ default: false,
316
+ },
317
+ showTags: {
318
+ type: Boolean,
319
+ default: false,
320
+ },
321
+ categoryPageName: {
322
+ type: String,
323
+ default: '',
324
+ },
325
+ enableFiveDollarsNotes: {
326
+ type: Boolean,
327
+ default: false,
328
+ },
329
+ isAdding: {
330
+ type: Boolean,
331
+ default: false,
332
+ },
333
+ isVisitor: {
334
+ type: Boolean,
335
+ default: true,
336
+ },
337
+ basketItems: {
338
+ type: Array,
339
+ default: () => ([]),
340
+ },
341
+ isBookmarked: {
342
+ type: Boolean,
343
+ default: false,
344
+ },
345
+ kvTrackFunction: {
346
+ type: Function,
347
+ required: true,
348
+ },
349
+ photoPath: {
350
+ type: String,
351
+ required: true,
352
+ },
353
+ showViewLoan: {
354
+ type: Boolean,
355
+ default: false,
356
+ },
357
+ externalLinks: {
358
+ type: Boolean,
359
+ default: false,
360
+ },
361
+ route: {
362
+ type: Object,
363
+ default: undefined,
364
+ },
365
+ userBalance: {
366
+ type: String,
367
+ default: undefined,
368
+ },
369
+ getCookie: {
370
+ type: Function,
371
+ default: undefined,
372
+ },
373
+ setCookie: {
374
+ type: Function,
375
+ default: undefined,
376
+ },
377
+ fiveDollarsSelected: {
378
+ type: Boolean,
379
+ default: false,
380
+ },
381
+ customCallouts: {
382
+ type: Array,
383
+ default: () => ([]),
384
+ },
385
+ useFullWidth: {
386
+ type: Boolean,
387
+ default: false,
388
+ },
389
+ largeCard: {
390
+ type: Boolean,
391
+ default: false,
392
+ },
393
+ isTeamPick: {
394
+ type: Boolean,
395
+ default: false,
396
+ },
397
+ combinedActivities: {
398
+ type: Array,
399
+ default: () => ([]),
400
+ },
401
+ errorMsg: {
402
+ type: String,
403
+ default: '',
404
+ },
405
+ enableHugeAmount: {
406
+ type: Boolean,
407
+ default: false,
408
+ },
409
+ primaryButtonText: {
410
+ type: String,
411
+ default: 'Lend',
412
+ },
413
+ secondaryButtonHandler: {
414
+ type: Function,
415
+ default: undefined,
416
+ },
417
+ secondaryButtonText: {
418
+ type: String,
419
+ default: 'Checkout now',
420
+ },
421
+ showContributors: {
422
+ type: Boolean,
423
+ default: false,
424
+ },
425
+ },
426
+ setup(props, { emit }) {
427
+ const {
428
+ allDataLoaded,
429
+ borrowerName,
430
+ city,
431
+ countryName,
432
+ distributionModel,
433
+ formattedLocation,
434
+ fundraisingPercent,
435
+ hasProgressData,
436
+ imageHash,
437
+ isLoading,
438
+ loanAmount,
439
+ loanBorrowerCount,
440
+ loanCallouts,
441
+ loanStatus,
442
+ loanUse,
443
+ mdiMapMarker,
444
+ readMorePath,
445
+ state,
446
+ tag,
447
+ unreservedAmount,
448
+ } = loanCardComputedProperties(props);
449
+
450
+ const {
451
+ clickReadMore,
452
+ } = loanCardMethods(props, emit);
453
+
454
+ return {
455
+ allDataLoaded,
456
+ borrowerName,
457
+ city,
458
+ countryName,
459
+ distributionModel,
460
+ formattedLocation,
461
+ fundraisingPercent,
462
+ hasProgressData,
463
+ imageHash,
464
+ isLoading,
465
+ loanAmount,
466
+ loanBorrowerCount,
467
+ loanCallouts,
468
+ loanStatus,
469
+ loanUse,
470
+ mdiMapMarker,
471
+ readMorePath,
472
+ state,
473
+ tag,
474
+ unreservedAmount,
475
+ clickReadMore,
476
+ };
477
+ },
478
+ computed: {
479
+ cardWidth() {
480
+ return this.useFullWidth ? '100%' : '374px';
481
+ },
482
+ imageAspectRatio() {
483
+ if (this.largeCard) {
484
+ return 5 / 8;
485
+ }
486
+ return 3 / 4;
487
+ },
488
+ imageDefaultWidth() {
489
+ return this.largeCard ? 480 : 336;
490
+ },
491
+ imageSizes() {
492
+ if (this.largeCard) {
493
+ return [{ width: 480 }];
494
+ }
495
+ return [
496
+ { width: this.imageDefaultWidth, viewSize: 1024 },
497
+ { width: this.imageDefaultWidth, viewSize: 768 },
498
+ { width: 416, viewSize: 480 },
499
+ { width: 374, viewSize: 414 },
500
+ { width: 335, viewSize: 375 },
501
+ ];
502
+ },
503
+ lendersNumber() {
504
+ return this.loan?.lenders?.totalCount ?? 0;
505
+ },
506
+ amountLent() {
507
+ const amount = this.loan?.loanFundraisingInfo?.fundedAmount ?? 0;
508
+ return numeral(parseFloat(amount)).format('$0,0');
509
+ },
510
+ },
511
+ };
512
+ </script>
513
+
514
+ <style lang="postcss" scoped>
515
+ /** Shared with KvWideLoanCard */
516
+ .loan-card-use:hover,
517
+ .loan-card-use:focus {
518
+ @apply tw-text-primary;
519
+ }
520
+ .loan-card-active-hover:hover .loan-card-use {
521
+ @apply tw-underline;
522
+ }
523
+ .loan-card-progress:hover,
524
+ .loan-card-progress:focus {
525
+ @apply tw-no-underline;
526
+ }
527
+ </style>
@@ -0,0 +1,135 @@
1
+ <template>
2
+ <div class="tw-flex tw-flex-col">
3
+ <div class="tw-flex tw-flex-col md:tw-flex-row md:tw-items-center tw-gap-0.5 md:tw-gap-1">
4
+ <div class="tw-flex tw-items-center tw-gap-1">
5
+ <kv-user-avatar
6
+ :lender-name="userDisplayName"
7
+ :lender-image-url="userImageUrl"
8
+ is-small
9
+ />
10
+ <div class="data-hj-suppress tw-font-medium">
11
+ {{ userDisplayName }}
12
+ </div>
13
+ </div>
14
+ <kv-text-input
15
+ :id="ADD_COMMENT_ID"
16
+ ref="input"
17
+ v-model="addCommentValue"
18
+ placeholder="Add a comment..."
19
+ class="data-hj-suppress tw-grow"
20
+ @keyup.enter="comment"
21
+ />
22
+ </div>
23
+ <div class="tw-flex tw-py-0.5 tw-gap-0.5">
24
+ <kv-button
25
+ variant="ghost"
26
+ class="tw-ml-auto"
27
+ @click="cancel"
28
+ >
29
+ Cancel
30
+ </kv-button>
31
+ <kv-button
32
+ variant="ghost"
33
+ :state="commentButtonState"
34
+ @click="comment"
35
+ >
36
+ Comment
37
+ </kv-button>
38
+ </div>
39
+ </div>
40
+ </template>
41
+
42
+ <script>
43
+ import { computed, ref } from 'vue-demi';
44
+ import KvButton from './KvButton.vue';
45
+ import KvTextInput from './KvTextInput.vue';
46
+ import KvUserAvatar from './KvUserAvatar.vue';
47
+
48
+ export const ADD_COMMENT_ID = 'add-comment-value';
49
+ export const ADD_COMMENT_EVENT = 'add-comment';
50
+ export const HIDE_INPUT_EVENT = 'hide-input';
51
+
52
+ export default {
53
+ name: 'KvCommentsAdd',
54
+ components: {
55
+ KvButton,
56
+ KvTextInput,
57
+ KvUserAvatar,
58
+ },
59
+ props: {
60
+ /**
61
+ * The full URL for the user image
62
+ */
63
+ userImageUrl: {
64
+ type: String,
65
+ default: '',
66
+ },
67
+ /**
68
+ * The name to display for the user
69
+ */
70
+ userDisplayName: {
71
+ type: String,
72
+ default: '',
73
+ },
74
+ /**
75
+ * Whether or not the comment is a reply
76
+ */
77
+ isReply: {
78
+ type: Boolean,
79
+ default: false,
80
+ },
81
+ },
82
+ emits: [ADD_COMMENT_EVENT, HIDE_INPUT_EVENT],
83
+ setup(props, { emit }) {
84
+ const addCommentValue = ref('');
85
+ const input = ref(null);
86
+
87
+ const commentButtonState = computed(() => (addCommentValue.value ? '' : 'disabled'));
88
+
89
+ const cancel = () => {
90
+ addCommentValue.value = '';
91
+ if (props.isReply) {
92
+ emit(HIDE_INPUT_EVENT);
93
+ }
94
+ };
95
+
96
+ const comment = () => {
97
+ emit(ADD_COMMENT_EVENT, addCommentValue.value);
98
+ addCommentValue.value = '';
99
+ if (props.isReply) {
100
+ emit(HIDE_INPUT_EVENT);
101
+ }
102
+ };
103
+
104
+ const focus = () => input.value.focus();
105
+
106
+ return {
107
+ ADD_COMMENT_ID,
108
+ addCommentValue,
109
+ commentButtonState,
110
+ cancel,
111
+ comment,
112
+ focus,
113
+ };
114
+ },
115
+ };
116
+ </script>
117
+
118
+ <style lang="postcss" scoped>
119
+ >>> input {
120
+ @apply tw-border-t-0 tw-border-r-0 tw-border-l-0 tw-rounded-none tw-p-0 tw-h-4;
121
+ }
122
+
123
+ >>> input:focus {
124
+ @apply tw-border-tertiary;
125
+ box-shadow: none;
126
+ }
127
+
128
+ >>> button > span {
129
+ @apply tw-min-h-0;
130
+ }
131
+
132
+ >>> button > span > span {
133
+ @apply tw-py-0 tw-px-0.5;
134
+ }
135
+ </style>