@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,365 @@
1
+ <template>
2
+ <transition
3
+ enter-active-class="tw-transition-opacity tw-duration-300"
4
+ leave-active-class="tw-transition-opacity tw-duration-300"
5
+ enter-class="tw-opacity-0"
6
+ enter-to-class="tw-opacity-full"
7
+ leave-class="tw-opacity-full"
8
+ leave-to-class="tw-opacity-0"
9
+ >
10
+ <!-- the screen -->
11
+ <div
12
+ v-show="visible"
13
+ class="screen"
14
+ @click.stop.prevent="onScreenClick"
15
+ >
16
+ <div>
17
+ <div
18
+ class="container"
19
+ >
20
+ <div
21
+ ref="kvCartModal"
22
+ tabindex="-1"
23
+ data-test="kv-cart-modal"
24
+ class="modal"
25
+ aria-modal="true"
26
+ aria-label="Added to basket"
27
+ @click.stop
28
+ >
29
+ <!-- header -->
30
+ <div
31
+ class="
32
+ tw-flex
33
+ tw-p-2.5 md:tw-px-4 md:tw-pt-4 md:tw-pb-3.5
34
+ "
35
+ >
36
+ <div class="tw-flex tw-flex-grow tw-gap-1 tw-items-center">
37
+ <!-- header -->
38
+ <slot name="header">
39
+ <kv-material-icon
40
+ class="tw-w-4 tw-h-4 tw-text-brand"
41
+ :icon="mdiCheckCircle"
42
+ />
43
+ <p class="tw-flex-1 tw-font-medium">
44
+ Added to basket
45
+ </p>
46
+ </slot>
47
+ <button
48
+ v-if="!preventClose"
49
+ class="
50
+ tw-grid tw-content-center tw-justify-center
51
+ tw-ml-auto
52
+ tw-w-6 tw-h-6 tw--m-2
53
+ hover:tw-text-action-highlight
54
+ "
55
+ @click.stop="hide('x-button')"
56
+ >
57
+ <kv-material-icon
58
+ class="tw-w-3 tw-h-3"
59
+ :icon="mdiClose"
60
+ />
61
+ <span class="tw-sr-only">Close</span>
62
+ </button>
63
+ </div>
64
+ </div>
65
+
66
+ <!-- body -->
67
+ <div
68
+ id="kvCartModalBody"
69
+ ref="kvCartModalBody"
70
+ class="modal__body"
71
+ >
72
+ <div>
73
+ <kv-borrower-image
74
+ class="tw-rounded loan-image"
75
+ :alt="borrowerImage.alt"
76
+ :aspect-ratio="borrowerImage.aspectRatio"
77
+ :default-image="borrowerImage.defaultImage"
78
+ :hash="borrowerImage.hash"
79
+ :images="borrowerImage.images"
80
+ :photo-path="photoPath"
81
+ />
82
+ </div>
83
+ <div class="tw-flex tw-items-center tw-justify-between tw-w-full tw-gap-1">
84
+ <div class="tw-flex tw-flex-col tw-h-full tw-justify-between">
85
+ <p class="tw-overflow-hidden tw-text-ellipsis tw-line-clamp-1">
86
+ {{ borrowerName }}
87
+ </p>
88
+ <p class="tw-p-1 tw-text-center tw-rounded tw-bg-secondary tw-text-h5 tw-w-max">
89
+ {{ borrowerCountry }}
90
+ </p>
91
+ </div>
92
+ <p> ${{ amount }}</p>
93
+ </div>
94
+ </div>
95
+
96
+ <!-- controls -->
97
+ <div
98
+ ref="controlsRef"
99
+ class="
100
+ tw-flex-shrink-0 tw-flex tw-justify-end tw-gap-x-2.5
101
+ tw-p-2.5 md:tw-px-4 md:tw-pb-4 tw-flex-col tw-gap-1
102
+ "
103
+ >
104
+ <kv-button
105
+ class="tw-w-full"
106
+ @click="handleClick('view-basket')"
107
+ >
108
+ View basket ({{ basketCount }})
109
+ </kv-button>
110
+ <kv-button
111
+ class="tw-w-full"
112
+ variant="secondary"
113
+ @click="handleClick('help-another-person')"
114
+ >
115
+ Help another person
116
+ </kv-button>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </transition>
123
+ </template>
124
+
125
+ <script>
126
+ import {
127
+ ref,
128
+ toRefs,
129
+ computed,
130
+ nextTick,
131
+ watch,
132
+ onBeforeUnmount,
133
+ onMounted,
134
+ } from 'vue-demi';
135
+ import { mdiClose, mdiCheckCircle } from '@mdi/js';
136
+ import { useFocusTrap } from '@vueuse/integrations/useFocusTrap';
137
+ import { hideOthers as makePageInert } from 'aria-hidden';
138
+ import { lockScroll, unlockScroll } from '../utils/scrollLock';
139
+ import { lockPrintSingleEl, unlockPrintSingleEl } from '../utils/printing';
140
+ import KvButton from './KvButton.vue';
141
+ import KvMaterialIcon from './KvMaterialIcon.vue';
142
+ import KvBorrowerImage from './KvBorrowerImage.vue';
143
+
144
+ /**
145
+ * Based on KvLightbox functionality
146
+ * */
147
+
148
+ export default {
149
+ components: {
150
+ KvMaterialIcon,
151
+ KvButton,
152
+ KvBorrowerImage,
153
+ },
154
+ props: {
155
+ /**
156
+ * Whether the dialog is open or not
157
+ * */
158
+ visible: {
159
+ type: Boolean,
160
+ default: false,
161
+ },
162
+ /**
163
+ * The dialog has no close X button, clicking the screen does not close,
164
+ * pressing ESC does not close.
165
+ * */
166
+ preventClose: {
167
+ type: Boolean,
168
+ default: false,
169
+ },
170
+ /**
171
+ * The number of loans in the basket
172
+ * */
173
+ basketCount: {
174
+ type: Number,
175
+ default: 0,
176
+ },
177
+ /**
178
+ * The loan added to the basket
179
+ * */
180
+ addedLoan: {
181
+ type: Object,
182
+ default: () => ({}),
183
+ },
184
+ /**
185
+ * The photo path for the borrower image
186
+ * */
187
+ photoPath: {
188
+ type: String,
189
+ default: '',
190
+ },
191
+ },
192
+ emits: [
193
+ 'cart-modal-closed',
194
+ ],
195
+ setup(props, { emit }) {
196
+ const {
197
+ visible,
198
+ preventClose,
199
+ addedLoan,
200
+ } = toRefs(props);
201
+
202
+ const kvCartModal = ref(null);
203
+ const kvCartModalBody = ref(null);
204
+ const controlsRef = ref(null);
205
+
206
+ const trapElements = computed(() => [
207
+ kvCartModal.value, // This cart modal
208
+ ]);
209
+ const {
210
+ activate: activateFocusTrap,
211
+ deactivate: deactivateFocusTrap,
212
+ } = useFocusTrap(trapElements, {
213
+ allowOutsideClick: true, // allow clicking outside the cart modal to close it
214
+ });
215
+
216
+ let makePageInertCallback = null;
217
+ let onKeyUp = null;
218
+
219
+ const hide = (closedBy = '') => {
220
+ // scroll any content inside the cart modal back to top
221
+ if (kvCartModal.value && kvCartModalBody.value) {
222
+ deactivateFocusTrap();
223
+ kvCartModalBody.value.scrollTop = 0;
224
+ unlockPrintSingleEl(kvCartModalBody.value);
225
+ }
226
+ unlockScroll();
227
+ if (makePageInertCallback) {
228
+ makePageInertCallback();
229
+ makePageInertCallback = null;
230
+ }
231
+ document.removeEventListener('keyup', onKeyUp);
232
+
233
+ /**
234
+ * Triggered when the cart modal is closed
235
+ * @event cart-modal-closed
236
+ * @type {Event}
237
+ */
238
+ emit('cart-modal-closed', { type: closedBy });
239
+ };
240
+
241
+ onKeyUp = (e) => {
242
+ if (!!e && e.key === 'Escape' && !preventClose.value) {
243
+ hide();
244
+ }
245
+ };
246
+
247
+ const onScreenClick = () => {
248
+ if (!preventClose.value) {
249
+ hide('background');
250
+ }
251
+ };
252
+
253
+ const show = () => {
254
+ if (visible.value) {
255
+ document.addEventListener('keyup', onKeyUp);
256
+
257
+ nextTick(() => {
258
+ if (kvCartModal.value && kvCartModalBody.value) {
259
+ activateFocusTrap();
260
+ makePageInertCallback = makePageInert(kvCartModal.value);
261
+ lockPrintSingleEl(kvCartModalBody.value);
262
+ }
263
+ lockScroll();
264
+ });
265
+ }
266
+ };
267
+
268
+ const borrowerName = computed(() => {
269
+ return addedLoan.value?.name ?? '';
270
+ });
271
+ const borrowerImage = computed(() => {
272
+ return {
273
+ alt: `Photo of ${borrowerName.value}`,
274
+ aspectRatio: 1,
275
+ defaultImage: { width: 300 },
276
+ hash: addedLoan.value.imageHash,
277
+ images: [
278
+ {
279
+ width: 300,
280
+ },
281
+ ],
282
+ };
283
+ });
284
+ const borrowerCountry = computed(() => {
285
+ return addedLoan.value.country ?? '';
286
+ });
287
+
288
+ const amount = computed(() => {
289
+ return addedLoan.value.amount ?? '';
290
+ });
291
+
292
+ const handleClick = (cta) => {
293
+ hide(cta);
294
+ };
295
+
296
+ watch(visible, () => {
297
+ if (visible.value) {
298
+ show();
299
+ } else {
300
+ hide();
301
+ }
302
+ });
303
+
304
+ onMounted(() => {
305
+ if (visible.value) {
306
+ show();
307
+ setTimeout(() => {
308
+ // Automatically close the cart modal after 10 seconds
309
+ emit('cart-modal-closed', { type: 'time' });
310
+ }, 10000);
311
+ }
312
+ });
313
+
314
+ onBeforeUnmount(() => hide());
315
+
316
+ return {
317
+ mdiClose,
318
+ mdiCheckCircle,
319
+ onKeyUp,
320
+ onScreenClick,
321
+ hide,
322
+ show,
323
+ controlsRef,
324
+
325
+ handleClick,
326
+ borrowerName,
327
+ borrowerImage,
328
+ borrowerCountry,
329
+ amount,
330
+ };
331
+ },
332
+ };
333
+ </script>
334
+
335
+ <style lang="postcss" scoped>
336
+ .screen {
337
+ @apply tw-z-modal tw-fixed tw-inset-0;
338
+ background-color: rgb(0, 0, 0, 0.2);
339
+ }
340
+
341
+ .modal {
342
+ @apply tw-bg-primary md:tw-absolute md:tw-right-0 tw-w-full tw-rounded-b;
343
+ max-height: 90%;
344
+ }
345
+
346
+ .modal__body {
347
+ @apply tw-flex tw-gap-2 tw-px-2.5 md:tw-px-4;
348
+ height: 3.75rem;
349
+ }
350
+
351
+ .loan-image {
352
+ width: 3.75rem;
353
+ }
354
+
355
+ .container {
356
+ @apply tw-absolute tw-inset-0;
357
+ }
358
+
359
+ @screen md {
360
+ .modal {
361
+ max-width: 24.5rem;
362
+ }
363
+ }
364
+
365
+ </style>
@@ -0,0 +1,203 @@
1
+ <template>
2
+ <div
3
+ :class="classes"
4
+ :style="styles"
5
+ >
6
+ <label
7
+ class="tw-inline-flex tw-items-center"
8
+ :class="{ 'tw-opacity-low': disabled }"
9
+ :for="uuid"
10
+ >
11
+ <input
12
+ v-bind="inputAttrs"
13
+ :id="uuid"
14
+ ref="checkboxRef"
15
+ class="tw-peer tw-appearance-none tw-w-max"
16
+ type="checkbox"
17
+ :checked="isChecked"
18
+ :value="value"
19
+ :disabled="disabled"
20
+ v-on="inputListeners"
21
+ @change.prevent="onChange"
22
+ >
23
+ <!-- checkbox square background -->
24
+ <div
25
+ class="
26
+ tw-w-3 tw-h-3 tw-mr-2
27
+ tw-flex-shrink-0
28
+ tw-rounded-sm
29
+ tw-border
30
+ tw-flex tw-justify-center tw-items-center tw-overflow-hidden
31
+ tw-transition-all tw-duration-100
32
+ peer-focus-visible:tw-ring-2 peer-focus-visible:tw-ring-action"
33
+ :class="{
34
+ 'tw-bg-white' : !isChecked,
35
+ 'tw-bg-action' : isChecked,
36
+ 'tw-border-secondary' : !isChecked && valid,
37
+ 'tw-border-action' : isChecked && valid,
38
+ 'tw-border-danger' : !valid,
39
+ }"
40
+ >
41
+ <!-- checkbox icon -->
42
+ <svg
43
+ v-if="isChecked"
44
+ class="tw-w-1.5 tw-h-auto"
45
+ viewBox="0 0 12 9"
46
+ fill="none"
47
+ xmlns="http://www.w3.org/2000/svg"
48
+ >
49
+ <!-- eslint-disable max-len -->
50
+ <path
51
+ d="M3.99975 6.79988L1.66642 4.46655C1.40642 4.20655 0.993086 4.20655 0.733086 4.46655C0.473086 4.72655 0.473086 5.13988 0.733086 5.39988L3.52642 8.19322C3.78642 8.45322 4.20642 8.45322 4.46642 8.19322L11.5331 1.13322C11.7931 0.873216 11.7931 0.459883 11.5331 0.199883C11.2731 -0.0601172 10.8598 -0.0601172 10.5998 0.199883L3.99975 6.79988Z"
52
+ fill="white"
53
+ />
54
+ <!-- eslint-enable max-len -->
55
+ </svg>
56
+ </div>
57
+ <!-- label -->
58
+ <div class="tw-flex-1 peer-focus-visible:tw-ring-2 peer-focus-visible:tw-ring-action">
59
+ <slot></slot>
60
+ </div>
61
+ </label>
62
+ </div>
63
+ </template>
64
+
65
+ <script>
66
+ import { nanoid } from 'nanoid';
67
+ import {
68
+ onMounted,
69
+ ref,
70
+ toRefs,
71
+ watch,
72
+ } from 'vue-demi';
73
+ import { useAttrs } from '../utils/attrs';
74
+
75
+ const emits = [
76
+ 'change',
77
+ 'update:modelValue',
78
+ ];
79
+
80
+ /**
81
+ * Use as you would an <input type="checkbox" />
82
+ * Does not handle 'indeterminate' state at this time.
83
+ */
84
+
85
+ export default {
86
+ inheritAttrs: false,
87
+
88
+ model: {
89
+ prop: 'modelValue',
90
+ event: 'update:modelValue',
91
+ },
92
+
93
+ props: {
94
+ /**
95
+ * Whether the checkbox is checked or not
96
+ * */
97
+ modelValue: {
98
+ type: [Boolean, Array],
99
+ default: false,
100
+ },
101
+ /**
102
+ * Prevents the checkbox from being toggled or focused
103
+ * */
104
+ disabled: {
105
+ type: Boolean,
106
+ default: false,
107
+ },
108
+ /**
109
+ * Value of the checkbox if v-model is an array
110
+ * */
111
+ value: {
112
+ type: [String, Boolean],
113
+ default: '',
114
+ },
115
+ /**
116
+ * When set to false, visually indicates to the user that the contents of the input need
117
+ * to be changed
118
+ * */
119
+ valid: {
120
+ type: Boolean,
121
+ default: true,
122
+ },
123
+ },
124
+ emits,
125
+ setup(props, context) {
126
+ const {
127
+ modelValue,
128
+ value,
129
+ } = toRefs(props);
130
+
131
+ const { emit } = context;
132
+ const uuid = ref(`kvc-${nanoid(10)}`);
133
+ const isChecked = ref(false);
134
+ const checkboxRef = ref(null);
135
+
136
+ const {
137
+ classes,
138
+ styles,
139
+ inputAttrs,
140
+ inputListeners,
141
+ } = useAttrs(context, emits);
142
+
143
+ const onChange = (event) => {
144
+ const inputChecked = event.target.checked;
145
+ let checkboxValue;
146
+
147
+ if (Array.isArray(modelValue.value)) {
148
+ // if the model is an array, add or remove our value from it
149
+ if (inputChecked) {
150
+ checkboxValue = [...modelValue.value, event.target.value];
151
+ } else {
152
+ checkboxValue = modelValue.value.filter((item) => item !== value.value);
153
+ }
154
+ } else {
155
+ checkboxValue = inputChecked;
156
+ }
157
+
158
+ // emit the change event to update the model
159
+ emit('change', checkboxValue);
160
+ emit('update:modelValue', checkboxValue);
161
+ };
162
+
163
+ const setChecked = () => {
164
+ if (Array.isArray(modelValue.value)) {
165
+ // if the model is array like <kv-checkbox v-model="['item1', 'item2']" value="item1">
166
+ isChecked.value = modelValue.value.includes(value.value);
167
+ } else {
168
+ // else it's a boolean like <kv-checkbox v-model="true">
169
+ isChecked.value = modelValue.value;
170
+ }
171
+ };
172
+
173
+ const focus = () => {
174
+ checkboxRef.focus();
175
+ };
176
+
177
+ const blur = () => {
178
+ checkboxRef.blur();
179
+ };
180
+
181
+ setChecked();
182
+ watch(modelValue, () => setChecked());
183
+ onMounted(() => {
184
+ uuid.value = `kvc-${nanoid(10)}`;
185
+ });
186
+
187
+ return {
188
+ uuid,
189
+ isChecked,
190
+ checkboxRef,
191
+ onChange,
192
+ setChecked,
193
+ focus,
194
+ blur,
195
+ classes,
196
+ styles,
197
+ inputAttrs,
198
+ inputListeners,
199
+ };
200
+ },
201
+ };
202
+
203
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <div class="tw-inline-flex">
3
+ <button
4
+ class="
5
+ tw-bg-secondary
6
+ hover:tw-bg-tertiary
7
+ tw-items-center
8
+ tw-flex
9
+ tw-flex-row
10
+ tw-cursor-pointer
11
+ tw-whitespace-nowrap
12
+ tw-rounded
13
+ tw-h-2
14
+ tw-py-2
15
+ tw-px-2
16
+ tw-select-none
17
+ "
18
+ @click="handleClick"
19
+ >
20
+ <div
21
+ class="tw-text-base tw-pr-1"
22
+ >
23
+ <slot></slot>
24
+ </div>
25
+ <kv-material-icon
26
+ :icon="mdiClose"
27
+ class="tw-w-2 tw-h-2"
28
+ :from-sprite="true"
29
+ />
30
+ </button>
31
+ </div>
32
+ </template>
33
+ <script>
34
+ import { mdiClose } from '@mdi/js';
35
+ import KvMaterialIcon from './KvMaterialIcon.vue';
36
+
37
+ export default {
38
+ name: 'KvChip',
39
+ components: { KvMaterialIcon },
40
+ emits: [
41
+ 'click-chip',
42
+ ],
43
+ setup() {
44
+ return {
45
+ mdiClose,
46
+ };
47
+ },
48
+ methods: {
49
+ handleClick() {
50
+ this.$emit('click-chip');
51
+ },
52
+ },
53
+ };
54
+ </script>