@kiva/kv-components 3.107.0 → 3.107.2
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.
- package/CHANGELOG.md +22 -0
- package/dist/components/.storybook/main.js +85 -0
- package/dist/components/.storybook/package.json +3 -0
- package/dist/components/.storybook/preview.js +61 -0
- package/dist/components/.storybook/tailwind.css +5 -0
- package/dist/components/KvAccordionItem.vue +130 -0
- package/dist/components/KvActivityRow.vue +33 -0
- package/dist/components/KvBorrowerImage.vue +179 -0
- package/dist/components/KvButton.vue +287 -0
- package/dist/components/KvCarousel.vue +297 -0
- package/dist/components/KvCartModal.vue +365 -0
- package/dist/components/KvCheckbox.vue +203 -0
- package/dist/components/KvChip.vue +54 -0
- package/dist/components/KvClassicLoanCard.vue +527 -0
- package/dist/components/KvCommentsAdd.vue +135 -0
- package/dist/components/KvCommentsContainer.vue +84 -0
- package/dist/components/KvCommentsHeartButton.vue +70 -0
- package/dist/components/KvCommentsList.vue +68 -0
- package/dist/components/KvCommentsListItem.vue +241 -0
- package/dist/components/KvCommentsReplyButton.vue +52 -0
- package/dist/components/KvContentfulImg.vue +273 -0
- package/dist/components/KvCountdownTimer.vue +59 -0
- package/dist/components/KvExpandable.vue +84 -0
- package/dist/components/KvExpandableQuestion.vue +120 -0
- package/dist/components/KvFlag.vue +120 -0
- package/dist/components/KvGrid.vue +28 -0
- package/dist/components/KvImpactDashboardHeader.vue +40 -0
- package/dist/components/KvInlineActivityCard.vue +55 -0
- package/dist/components/KvInlineActivityFeed.vue +38 -0
- package/dist/components/KvIntroductionLoanCard.vue +446 -0
- package/dist/components/KvLendAmountButton.vue +65 -0
- package/dist/components/KvLendCta.vue +451 -0
- package/dist/components/KvLightbox.vue +334 -0
- package/dist/components/KvLineGraph.vue +128 -0
- package/dist/components/KvLoadingPlaceholder.vue +38 -0
- package/dist/components/KvLoadingSpinner.vue +81 -0
- package/dist/components/KvLoanActivities.vue +268 -0
- package/dist/components/KvLoanBookmark.vue +39 -0
- package/dist/components/KvLoanCallouts.vue +53 -0
- package/dist/components/KvLoanProgressGroup.vue +76 -0
- package/dist/components/KvLoanTag.vue +88 -0
- package/dist/components/KvLoanTeamPick.vue +44 -0
- package/dist/components/KvLoanUse.vue +92 -0
- package/dist/components/KvMap.vue +599 -0
- package/dist/components/KvMaterialIcon.vue +47 -0
- package/dist/components/KvPageContainer.vue +15 -0
- package/dist/components/KvPagination.vue +198 -0
- package/dist/components/KvPieChart.vue +257 -0
- package/dist/components/KvPopper.vue +178 -0
- package/dist/components/KvProgressBar.vue +149 -0
- package/dist/components/KvRadio.vue +198 -0
- package/dist/components/KvSelect.vue +114 -0
- package/dist/components/KvSideSheet.vue +134 -0
- package/dist/components/KvSwitch.vue +143 -0
- package/dist/components/KvTab.vue +90 -0
- package/dist/components/KvTabPanel.vue +64 -0
- package/dist/components/KvTabs.vue +182 -0
- package/dist/components/KvTextInput.vue +247 -0
- package/dist/components/KvTextLink.vue +138 -0
- package/dist/components/KvThemeProvider.vue +122 -0
- package/dist/components/KvToast.vue +221 -0
- package/dist/components/KvTooltip.vue +168 -0
- package/dist/components/KvTreeMapChart.vue +229 -0
- package/dist/components/KvUserAvatar.vue +132 -0
- package/dist/components/KvVerticalCarousel.vue +156 -0
- package/dist/components/KvVotingCard.vue +160 -0
- package/dist/components/KvVotingCardV2.vue +154 -0
- package/dist/components/KvWideLoanCard.vue +432 -0
- package/dist/components/stories/Forms.stories.js +62 -0
- package/dist/components/stories/KvAccordionItem.stories.js +24 -0
- package/dist/components/stories/KvActivityRow.stories.js +25 -0
- package/dist/components/stories/KvBorrowerImage.stories.js +68 -0
- package/dist/components/stories/KvButton.stories.js +144 -0
- package/dist/components/stories/KvCarousel.stories.js +426 -0
- package/dist/components/stories/KvCartModal.stories.js +54 -0
- package/dist/components/stories/KvCheckbox.stories.js +163 -0
- package/dist/components/stories/KvChip.stories.js +43 -0
- package/dist/components/stories/KvClassicLoanCard.stories.js +480 -0
- package/dist/components/stories/KvCommentsAdd.stories.js +32 -0
- package/dist/components/stories/KvCommentsContainer.stories.js +42 -0
- package/dist/components/stories/KvCommentsHeartButton.stories.js +25 -0
- package/dist/components/stories/KvCommentsList.stories.js +39 -0
- package/dist/components/stories/KvCommentsListItem.stories.js +45 -0
- package/dist/components/stories/KvCommentsReplyButton.stories.js +21 -0
- package/dist/components/stories/KvContentfulImg.stories.js +196 -0
- package/dist/components/stories/KvCountdownTimer.stories.js +30 -0
- package/dist/components/stories/KvExpandableQuestion.stories.js +129 -0
- package/dist/components/stories/KvFlag.stories.js +36 -0
- package/dist/components/stories/KvGrid.stories.js +97 -0
- package/dist/components/stories/KvImpactDashboardHeader.stories.js +22 -0
- package/dist/components/stories/KvInlineActivityCard.stories.js +69 -0
- package/dist/components/stories/KvInlineActivityFeed.stories.js +76 -0
- package/dist/components/stories/KvIntroductionLoanCard.stories.js +208 -0
- package/dist/components/stories/KvLendAmountButton.stories.js +31 -0
- package/dist/components/stories/KvLendCta.stories.js +177 -0
- package/dist/components/stories/KvLightbox.stories.js +304 -0
- package/dist/components/stories/KvLineGraph.stories.js +52 -0
- package/dist/components/stories/KvLoadingPlaceholder.stories.js +17 -0
- package/dist/components/stories/KvLoadingSpinner.stories.js +52 -0
- package/dist/components/stories/KvLoanActivities.stories.js +104 -0
- package/dist/components/stories/KvLoanBookmark.stories.js +22 -0
- package/dist/components/stories/KvLoanCallouts.stories.js +22 -0
- package/dist/components/stories/KvLoanProgressGroup.stories.js +29 -0
- package/dist/components/stories/KvLoanTag.stories.js +61 -0
- package/dist/components/stories/KvLoanTeamPick.stories.js +20 -0
- package/dist/components/stories/KvLoanUse.stories.js +60 -0
- package/dist/components/stories/KvMap.stories.js +121 -0
- package/dist/components/stories/KvMaterialIcon.stories.js +201 -0
- package/dist/components/stories/KvPageContainer.stories.js +50 -0
- package/dist/components/stories/KvPagination.stories.js +70 -0
- package/dist/components/stories/KvPieChart.stories.js +47 -0
- package/dist/components/stories/KvProgressBar.stories.js +53 -0
- package/dist/components/stories/KvRadio.stories.js +140 -0
- package/dist/components/stories/KvSelect.stories.js +125 -0
- package/dist/components/stories/KvSideSheet.stories.js +50 -0
- package/dist/components/stories/KvSwitch.stories.js +66 -0
- package/dist/components/stories/KvTabs.stories.js +106 -0
- package/dist/components/stories/KvTextInput.stories.js +194 -0
- package/dist/components/stories/KvTextLink.stories.js +55 -0
- package/dist/components/stories/KvThemeProvider.stories.js +178 -0
- package/dist/components/stories/KvToast.stories.js +117 -0
- package/dist/components/stories/KvTooltip.stories.js +26 -0
- package/dist/components/stories/KvTreeMapChart.stories.js +42 -0
- package/dist/components/stories/KvUserAvatar.stories.js +47 -0
- package/dist/components/stories/KvVerticalCarousel.stories.js +168 -0
- package/dist/components/stories/KvVotingCard.stories.js +33 -0
- package/dist/components/stories/KvVotingCardV2.stories.js +89 -0
- package/dist/components/stories/KvWideLoanCard.stories.js +292 -0
- package/dist/components/stories/StyleguidePrimitives.stories.js +499 -0
- package/dist/components/stories/StyleguideProse.stories.js +215 -0
- package/dist/data/countries-borders.json +1 -0
- package/dist/data/ne_110m_admin_0_countries.json +1 -0
- package/dist/utils/Alea.js +9 -0
- package/dist/utils/attrs.js +7 -0
- package/dist/utils/carousels.js +8 -0
- package/dist/{attrs.js → utils/chunk-3HK4G4NT.js} +1 -0
- package/dist/{loanCard.js → utils/chunk-55HF2ORX.js} +1 -0
- package/dist/{expander.js → utils/chunk-AY3PR5S4.js} +3 -2
- package/dist/{carousels.js → utils/chunk-AZPWOFD5.js} +1 -0
- package/dist/{printing.js → utils/chunk-B5J5WLAH.js} +1 -0
- package/dist/{Alea.js → utils/chunk-GPSH6OPA.js} +2 -1
- package/dist/{scrollLock.js → utils/chunk-HIY5IW65.js} +2 -1
- package/dist/{treemap.js → utils/chunk-MSMZIN54.js} +1 -0
- package/dist/{imageUtils.js → utils/chunk-OXJCCNNW.js} +1 -0
- package/dist/{touchEvents.js → utils/chunk-S3MABILA.js} +3 -2
- package/dist/{mapUtils.js → utils/chunk-VIGEMAKO.js} +5 -4
- package/dist/utils/chunk-YCNMJ4YV.js +37 -0
- package/dist/{loanUtils.js → utils/chunk-YFEC5ODJ.js} +7 -6
- package/dist/utils/expander.js +9 -0
- package/dist/utils/imageUtils.js +9 -0
- package/dist/utils/index.cjs +1118 -0
- package/dist/utils/index.js +166 -0
- package/dist/utils/loanCard.js +9 -0
- package/dist/utils/loanUtils.js +23 -0
- package/dist/utils/mapUtils.js +15 -0
- package/dist/utils/printing.js +9 -0
- package/dist/utils/scrollLock.js +13 -0
- package/dist/{throttle.js → utils/throttle.js} +1 -0
- package/dist/utils/touchEvents.js +11 -0
- package/dist/utils/treemap.js +7 -0
- package/package.json +11 -7
- package/utils/index.js +14 -0
- package/index.js +0 -3
- /package/dist/{Alea.cjs → utils/Alea.cjs} +0 -0
- /package/dist/{attrs.cjs → utils/attrs.cjs} +0 -0
- /package/dist/{carousels.cjs → utils/carousels.cjs} +0 -0
- /package/dist/{chunk-HV3AUBFT.js → utils/chunk-HV3AUBFT.js} +0 -0
- /package/dist/{expander.cjs → utils/expander.cjs} +0 -0
- /package/dist/{imageUtils.cjs → utils/imageUtils.cjs} +0 -0
- /package/dist/{loanCard.cjs → utils/loanCard.cjs} +0 -0
- /package/dist/{loanUtils.cjs → utils/loanUtils.cjs} +0 -0
- /package/dist/{mapUtils.cjs → utils/mapUtils.cjs} +0 -0
- /package/dist/{printing.cjs → utils/printing.cjs} +0 -0
- /package/dist/{scrollLock.cjs → utils/scrollLock.cjs} +0 -0
- /package/dist/{throttle.cjs → utils/throttle.cjs} +0 -0
- /package/dist/{touchEvents.cjs → utils/touchEvents.cjs} +0 -0
- /package/dist/{treemap.cjs → utils/treemap.cjs} +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
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="
|
|
14
|
+
tw-z-modal
|
|
15
|
+
tw-fixed
|
|
16
|
+
tw-inset-0
|
|
17
|
+
tw-bg-black
|
|
18
|
+
tw-bg-opacity-[75%]
|
|
19
|
+
"
|
|
20
|
+
@click.stop.prevent="onScreenClick"
|
|
21
|
+
>
|
|
22
|
+
<div>
|
|
23
|
+
<div
|
|
24
|
+
class="
|
|
25
|
+
tw-flex
|
|
26
|
+
tw-absolute
|
|
27
|
+
tw-inset-0
|
|
28
|
+
"
|
|
29
|
+
:class="{
|
|
30
|
+
'md:tw-px-2' : variant === 'lightbox',
|
|
31
|
+
'tw-px-2' : variant === 'alert',
|
|
32
|
+
}"
|
|
33
|
+
>
|
|
34
|
+
<!-- the lightbox itself -->
|
|
35
|
+
<div
|
|
36
|
+
ref="kvLightbox"
|
|
37
|
+
tabindex="-1"
|
|
38
|
+
data-test="kv-lightbox"
|
|
39
|
+
class="
|
|
40
|
+
tw-bg-primary
|
|
41
|
+
tw-flex tw-flex-col
|
|
42
|
+
tw-mx-auto md:tw-my-auto
|
|
43
|
+
|
|
44
|
+
"
|
|
45
|
+
:class="{
|
|
46
|
+
'tw-w-full md:tw-w-auto' : variant === 'lightbox',
|
|
47
|
+
'tw-mt-auto md:tw-my-auto' : variant === 'lightbox',
|
|
48
|
+
'tw-min-h-half-screen md:tw-min-h-0' : variant === 'lightbox',
|
|
49
|
+
'tw-rounded-t md:tw-rounded' : variant === 'lightbox',
|
|
50
|
+
'tw-my-auto tw-rounded' : variant === 'alert',
|
|
51
|
+
}"
|
|
52
|
+
style="max-width: 55.55rem; max-height: 90%"
|
|
53
|
+
aria-modal="true"
|
|
54
|
+
:aria-label="title ? title : null"
|
|
55
|
+
:aria-describedby="variant === 'alert' ? 'kvLightboxBody' : null"
|
|
56
|
+
:role="role"
|
|
57
|
+
@click.stop
|
|
58
|
+
>
|
|
59
|
+
<!-- header -->
|
|
60
|
+
<div
|
|
61
|
+
class="
|
|
62
|
+
tw-flex
|
|
63
|
+
tw-p-2.5 md:tw-px-4 md:tw-pt-4 md:tw-pb-3.5
|
|
64
|
+
"
|
|
65
|
+
>
|
|
66
|
+
<div class="tw-flex-grow">
|
|
67
|
+
<!-- @slot header -->
|
|
68
|
+
<slot name="header">
|
|
69
|
+
<h2 class="tw-text-h3 tw-flex-1">
|
|
70
|
+
{{ title }}
|
|
71
|
+
</h2>
|
|
72
|
+
</slot>
|
|
73
|
+
</div>
|
|
74
|
+
<button
|
|
75
|
+
v-if="!preventClose"
|
|
76
|
+
class="
|
|
77
|
+
tw-grid tw-content-center tw-justify-center
|
|
78
|
+
tw-ml-auto
|
|
79
|
+
tw-w-6 tw-h-6 tw--m-2
|
|
80
|
+
hover:tw-text-action-highlight
|
|
81
|
+
"
|
|
82
|
+
@click.stop="hide('close-x')"
|
|
83
|
+
>
|
|
84
|
+
<kv-material-icon
|
|
85
|
+
class="tw-w-3 tw-h-3"
|
|
86
|
+
:icon="mdiClose"
|
|
87
|
+
/>
|
|
88
|
+
<span class="tw-sr-only">Close</span>
|
|
89
|
+
</button>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<!-- body -->
|
|
93
|
+
<div
|
|
94
|
+
id="kvLightboxBody"
|
|
95
|
+
ref="kvLightboxBody"
|
|
96
|
+
class="
|
|
97
|
+
tw-flex-1
|
|
98
|
+
tw-px-2.5 md:tw-px-4
|
|
99
|
+
tw-pb-2.5 md:tw-pb-4
|
|
100
|
+
tw-overflow-auto
|
|
101
|
+
"
|
|
102
|
+
>
|
|
103
|
+
<!-- @slot default -->
|
|
104
|
+
<slot></slot>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- controls -->
|
|
108
|
+
<div
|
|
109
|
+
v-if="$slots.controls"
|
|
110
|
+
ref="controlsRef"
|
|
111
|
+
class="
|
|
112
|
+
tw-flex-shrink-0 tw-flex tw-justify-end tw-gap-x-2.5
|
|
113
|
+
tw-p-2.5 md:tw-px-4 md:tw-pb-4 md:tw-pt-1
|
|
114
|
+
"
|
|
115
|
+
>
|
|
116
|
+
<!-- @slot controls -->
|
|
117
|
+
<slot name="controls"></slot>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</transition>
|
|
124
|
+
</template>
|
|
125
|
+
|
|
126
|
+
<script>
|
|
127
|
+
import {
|
|
128
|
+
ref,
|
|
129
|
+
toRefs,
|
|
130
|
+
computed,
|
|
131
|
+
nextTick,
|
|
132
|
+
watch,
|
|
133
|
+
onBeforeUnmount,
|
|
134
|
+
onMounted,
|
|
135
|
+
} from 'vue-demi';
|
|
136
|
+
import { mdiClose } from '@mdi/js';
|
|
137
|
+
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap';
|
|
138
|
+
import { hideOthers as makePageInert } from 'aria-hidden';
|
|
139
|
+
import { lockScroll, unlockScroll } from '../utils/scrollLock';
|
|
140
|
+
import { lockPrintSingleEl, unlockPrintSingleEl } from '../utils/printing';
|
|
141
|
+
|
|
142
|
+
import KvMaterialIcon from './KvMaterialIcon.vue';
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Alert or a lightbox
|
|
146
|
+
* Accessibility: https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal
|
|
147
|
+
*
|
|
148
|
+
* - [x] Tab and Shift + Tab do not move focus outside the dialog
|
|
149
|
+
* - [x] focus is initially set on the first focusable element (close button).
|
|
150
|
+
* - [x] focus is returned to the element that opened the dialog on close
|
|
151
|
+
* - [x] role = dialog
|
|
152
|
+
* - [x] aria-label is set to its title.
|
|
153
|
+
* - [x] adds aria-hidden=true to all elements other than this dialog when open. - https://github.com/theKashey/vue-focus-lock/issues/16
|
|
154
|
+
* - [x] scrolling only scrolls the lightbox contents, not the page itself
|
|
155
|
+
|
|
156
|
+
* Alert dialog - https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_alertdialog_role
|
|
157
|
+
*
|
|
158
|
+
* - [x] focus moves to the first non-destructive control, rather than the close button
|
|
159
|
+
* - [x] role = alertDialog
|
|
160
|
+
* - [x] aria-describedby is set to the id of the dialog body
|
|
161
|
+
*
|
|
162
|
+
* Printing
|
|
163
|
+
*
|
|
164
|
+
* - [x] Only prints the contents of the lightbox when open
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
export default {
|
|
168
|
+
components: {
|
|
169
|
+
KvMaterialIcon,
|
|
170
|
+
},
|
|
171
|
+
props: {
|
|
172
|
+
/**
|
|
173
|
+
* Whether the dialog is open or not
|
|
174
|
+
* */
|
|
175
|
+
visible: {
|
|
176
|
+
type: Boolean,
|
|
177
|
+
default: false,
|
|
178
|
+
},
|
|
179
|
+
/**
|
|
180
|
+
* Appearance and role of the lightbox
|
|
181
|
+
* @values lightbox, alert
|
|
182
|
+
* */
|
|
183
|
+
variant: {
|
|
184
|
+
type: String,
|
|
185
|
+
default: 'lightbox',
|
|
186
|
+
validator(value) {
|
|
187
|
+
return ['lightbox', 'alert'].includes(value);
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
/**
|
|
191
|
+
* The title of the dialog which describes the dialog to screenreaders, and if no
|
|
192
|
+
* content is in the `header` slot, will be displayed at the top of the lightbox.
|
|
193
|
+
* */
|
|
194
|
+
title: {
|
|
195
|
+
type: String,
|
|
196
|
+
required: true,
|
|
197
|
+
},
|
|
198
|
+
/**
|
|
199
|
+
* The dialog has no close X button, clicking the screen does not close,
|
|
200
|
+
* pressing ESC does not close.
|
|
201
|
+
* */
|
|
202
|
+
preventClose: {
|
|
203
|
+
type: Boolean,
|
|
204
|
+
default: false,
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
emits: [
|
|
208
|
+
'lightbox-closed',
|
|
209
|
+
],
|
|
210
|
+
setup(props, { emit }) {
|
|
211
|
+
const {
|
|
212
|
+
visible,
|
|
213
|
+
variant,
|
|
214
|
+
preventClose,
|
|
215
|
+
} = toRefs(props);
|
|
216
|
+
|
|
217
|
+
const kvLightbox = ref(null);
|
|
218
|
+
const kvLightboxBody = ref(null);
|
|
219
|
+
const controlsRef = ref(null);
|
|
220
|
+
const activateFocusTrap = ref(null);
|
|
221
|
+
const deactivateFocusTrap = ref(null);
|
|
222
|
+
|
|
223
|
+
// Ensure the lightbox ref isn't null
|
|
224
|
+
nextTick(() => {
|
|
225
|
+
const {
|
|
226
|
+
activate,
|
|
227
|
+
deactivate,
|
|
228
|
+
} = useFocusTrap([
|
|
229
|
+
kvLightbox.value, // This lightbox
|
|
230
|
+
'[role="alert"]', // Any open toasts/alerts on the page
|
|
231
|
+
], {
|
|
232
|
+
allowOutsideClick: true, // allow clicking outside the lightbox to close it
|
|
233
|
+
});
|
|
234
|
+
activateFocusTrap.value = activate;
|
|
235
|
+
deactivateFocusTrap.value = deactivate;
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
let makePageInertCallback = null;
|
|
239
|
+
let onKeyUp = null;
|
|
240
|
+
|
|
241
|
+
const role = computed(() => {
|
|
242
|
+
if (variant.value === 'alert') {
|
|
243
|
+
return 'alertdialog';
|
|
244
|
+
}
|
|
245
|
+
return 'dialog';
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const hide = (closedBy = '') => {
|
|
249
|
+
// scroll any content inside the lightbox back to top
|
|
250
|
+
if (kvLightbox.value && kvLightboxBody.value) {
|
|
251
|
+
deactivateFocusTrap.value?.();
|
|
252
|
+
kvLightboxBody.value.scrollTop = 0;
|
|
253
|
+
unlockPrintSingleEl(kvLightboxBody.value);
|
|
254
|
+
}
|
|
255
|
+
unlockScroll();
|
|
256
|
+
if (makePageInertCallback) {
|
|
257
|
+
makePageInertCallback();
|
|
258
|
+
makePageInertCallback = null;
|
|
259
|
+
}
|
|
260
|
+
document.removeEventListener('keyup', onKeyUp);
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Triggered when the lightbox is closed
|
|
264
|
+
* @event lightbox-closed
|
|
265
|
+
* @type {Event}
|
|
266
|
+
*/
|
|
267
|
+
emit('lightbox-closed', { type: closedBy });
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
onKeyUp = (e) => {
|
|
271
|
+
if (!!e && e.key === 'Escape' && !preventClose.value) {
|
|
272
|
+
hide();
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const onScreenClick = () => {
|
|
277
|
+
if (!preventClose.value) {
|
|
278
|
+
hide('background-click');
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const show = () => {
|
|
283
|
+
if (visible.value) {
|
|
284
|
+
document.addEventListener('keyup', onKeyUp);
|
|
285
|
+
|
|
286
|
+
nextTick(() => {
|
|
287
|
+
if (kvLightbox.value && kvLightboxBody.value) {
|
|
288
|
+
activateFocusTrap.value?.();
|
|
289
|
+
makePageInertCallback = makePageInert(kvLightbox.value);
|
|
290
|
+
lockPrintSingleEl(kvLightboxBody.value);
|
|
291
|
+
}
|
|
292
|
+
lockScroll();
|
|
293
|
+
|
|
294
|
+
// alerts should send focus to the first actionable item in the controls
|
|
295
|
+
if (variant.value === 'alert') {
|
|
296
|
+
const firstControlEl = controlsRef.value.querySelector('button');
|
|
297
|
+
if (firstControlEl) {
|
|
298
|
+
firstControlEl.focus();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
watch(visible, () => {
|
|
306
|
+
if (visible.value) {
|
|
307
|
+
show();
|
|
308
|
+
} else {
|
|
309
|
+
hide();
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
onMounted(() => {
|
|
314
|
+
if (visible.value) {
|
|
315
|
+
show();
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
onBeforeUnmount(() => hide());
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
mdiClose,
|
|
323
|
+
role,
|
|
324
|
+
kvLightbox,
|
|
325
|
+
kvLightboxBody,
|
|
326
|
+
onKeyUp,
|
|
327
|
+
onScreenClick,
|
|
328
|
+
hide,
|
|
329
|
+
show,
|
|
330
|
+
controlsRef,
|
|
331
|
+
};
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
</script>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tw-h-full tw-w-full tw-p-2.5">
|
|
3
|
+
<figure
|
|
4
|
+
class="tw-w-full tw-relative"
|
|
5
|
+
:style="{ height: graphHeight }"
|
|
6
|
+
>
|
|
7
|
+
<div
|
|
8
|
+
class="tw-w-full tw-h-full tw-bg-marigold-2 tw-opacity-low"
|
|
9
|
+
:style="{ clipPath: `polygon(${shade}, 100% 100%, 0% 100%)` }"
|
|
10
|
+
></div>
|
|
11
|
+
<div
|
|
12
|
+
class="tw-absolute tw-top-0 tw-w-full tw-h-full tw-bg-marigold-2"
|
|
13
|
+
:style="{ clipPath: `polygon(${line})` }"
|
|
14
|
+
></div>
|
|
15
|
+
<span
|
|
16
|
+
v-for="point in normalizedPoints"
|
|
17
|
+
:key="point.x"
|
|
18
|
+
class="
|
|
19
|
+
tw-absolute
|
|
20
|
+
tw-w-2
|
|
21
|
+
tw-h-2
|
|
22
|
+
tw-border
|
|
23
|
+
tw-border-white
|
|
24
|
+
tw-bg-marigold-2
|
|
25
|
+
tw-rounded-full
|
|
26
|
+
"
|
|
27
|
+
:style="{ left: `${point.x}%`, top: `${point.y}%`, transform: 'translate(-50%, -50%)' }"
|
|
28
|
+
></span>
|
|
29
|
+
<template v-for="point in normalizedPoints">
|
|
30
|
+
<span
|
|
31
|
+
v-if="point.label"
|
|
32
|
+
:key="point.label"
|
|
33
|
+
class="tw-absolute"
|
|
34
|
+
:style="{ left: `${point.x}%`, bottom: '-3rem', transform: 'translate(-50%, -50%)' }"
|
|
35
|
+
>
|
|
36
|
+
{{ point.label }}
|
|
37
|
+
</span>
|
|
38
|
+
</template>
|
|
39
|
+
</figure>
|
|
40
|
+
<h4
|
|
41
|
+
v-if="axisLabel"
|
|
42
|
+
class="tw-text-center"
|
|
43
|
+
:class="{ 'tw-pt-1': !hasValueLabels, 'tw-pt-6': hasValueLabels }"
|
|
44
|
+
>
|
|
45
|
+
{{ axisLabel }}
|
|
46
|
+
</h4>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<script>
|
|
51
|
+
import { computed, toRefs } from 'vue-demi';
|
|
52
|
+
|
|
53
|
+
export default {
|
|
54
|
+
props: {
|
|
55
|
+
/**
|
|
56
|
+
* Array of objects like [{ value: 10, label: '2014' }, { value: 20, label: '2015' }]
|
|
57
|
+
*/
|
|
58
|
+
points: {
|
|
59
|
+
type: Array,
|
|
60
|
+
required: true,
|
|
61
|
+
},
|
|
62
|
+
/**
|
|
63
|
+
* The optional label to show below the graph on the x-axis
|
|
64
|
+
*/
|
|
65
|
+
axisLabel: {
|
|
66
|
+
type: String,
|
|
67
|
+
default: '',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
setup(props) {
|
|
71
|
+
const { points, axisLabel } = toRefs(props);
|
|
72
|
+
|
|
73
|
+
// Get step to use on x-axis
|
|
74
|
+
const xIncrement = Math.round(100 / (points.value.length - 1));
|
|
75
|
+
|
|
76
|
+
// Find the largest value to be used as the scale of the graph
|
|
77
|
+
const largestY = computed(() => {
|
|
78
|
+
return points.value.reduce((prev, current) => {
|
|
79
|
+
return prev > current.value ? prev : current.value;
|
|
80
|
+
}, 0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Find the largest value to be used as the scale of the graph
|
|
84
|
+
const hasValueLabels = computed(() => {
|
|
85
|
+
return points.value.reduce((prev, current) => {
|
|
86
|
+
return prev || !!current.label;
|
|
87
|
+
}, false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Convert single values to points using increment and largest value
|
|
91
|
+
const normalizedPoints = computed(() => {
|
|
92
|
+
return points.value.reduce((prev, next, i) => {
|
|
93
|
+
prev.push({
|
|
94
|
+
x: i * xIncrement,
|
|
95
|
+
y: 100 - ((next.value / largestY.value) * 100),
|
|
96
|
+
label: next.label,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return prev;
|
|
100
|
+
}, []);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Used for drawing the shading under the line
|
|
104
|
+
const shade = computed(() => (normalizedPoints.value.map(({ x, y }) => `${x}% ${y}%`).join(',')));
|
|
105
|
+
|
|
106
|
+
// Used for drawing the line
|
|
107
|
+
const line = computed(() => {
|
|
108
|
+
const topLine = normalizedPoints.value.map(({ x, y }) => `${x}% ${y + 0.3}%`).join(',');
|
|
109
|
+
const bottomLine = [...normalizedPoints.value].reverse().map(({ x, y }) => `${x}% ${y - 0.3}%`).join(',');
|
|
110
|
+
return `${topLine}, ${bottomLine}`;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const graphHeight = computed(() => {
|
|
114
|
+
const labelSpace = (axisLabel.value ? 2 : 0) + (hasValueLabels.value ? 2 : 0);
|
|
115
|
+
|
|
116
|
+
return `calc(100% - ${labelSpace}rem)`;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
hasValueLabels,
|
|
121
|
+
graphHeight,
|
|
122
|
+
normalizedPoints,
|
|
123
|
+
shade,
|
|
124
|
+
line,
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
</script>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="
|
|
4
|
+
loading-placeholder
|
|
5
|
+
tw-w-full tw-h-full
|
|
6
|
+
tw-relative
|
|
7
|
+
tw-overflow-hidden
|
|
8
|
+
tw-bg-tertiary
|
|
9
|
+
tw-rounded-sm
|
|
10
|
+
"
|
|
11
|
+
>
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<style scoped>
|
|
16
|
+
.loading-placeholder::before {
|
|
17
|
+
content: '';
|
|
18
|
+
display: block;
|
|
19
|
+
position: absolute;
|
|
20
|
+
height: 100%;
|
|
21
|
+
width: 100%;
|
|
22
|
+
top: 0;
|
|
23
|
+
transform: translateX(100%);
|
|
24
|
+
background:
|
|
25
|
+
linear-gradient(to right, transparent 0%, rgb(var(--bg-secondary)) 50%, transparent 100%);
|
|
26
|
+
animation: loading-placeholder 1.5s infinite cubic-bezier(0.4, 0, 0.2, 1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@keyframes loading-placeholder {
|
|
30
|
+
from {
|
|
31
|
+
transform: translateX(-100%);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
to {
|
|
35
|
+
transform: translateX(100%);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
class="tw-animate-spin-eased"
|
|
4
|
+
:class="{
|
|
5
|
+
'tw-h-3 tw-w-3'
|
|
6
|
+
: size === 'small',
|
|
7
|
+
'tw-h-4 tw-w-4'
|
|
8
|
+
: size === 'medium',
|
|
9
|
+
'tw-h-8 tw-w-8'
|
|
10
|
+
: size === 'large',
|
|
11
|
+
'tw-text-brand'
|
|
12
|
+
: color === 'brand',
|
|
13
|
+
'tw-text-white'
|
|
14
|
+
: color === 'white',
|
|
15
|
+
'tw-text-black'
|
|
16
|
+
: color === 'black',
|
|
17
|
+
}"
|
|
18
|
+
fill="none"
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
viewBox="0 0 24 24"
|
|
21
|
+
>
|
|
22
|
+
<g
|
|
23
|
+
clip-path="url(#clip0)"
|
|
24
|
+
stroke-width="2"
|
|
25
|
+
>
|
|
26
|
+
<circle
|
|
27
|
+
class="tw-stroke-current tw-text-tertiary tw-opacity-low"
|
|
28
|
+
cx="12"
|
|
29
|
+
cy="12"
|
|
30
|
+
r="8"
|
|
31
|
+
transform="rotate(135 12 12)"
|
|
32
|
+
/>
|
|
33
|
+
<path
|
|
34
|
+
d="M17.447 17.866c3.24-3.24 3.334-8.399.21-11.523-3.125-3.124-8.284-3.03-11.524.21"
|
|
35
|
+
stroke="currentColor"
|
|
36
|
+
stroke-linecap="round"
|
|
37
|
+
/>
|
|
38
|
+
</g>
|
|
39
|
+
<defs>
|
|
40
|
+
<clipPath id="clip0">
|
|
41
|
+
<path
|
|
42
|
+
fill="#fff"
|
|
43
|
+
d="M0 0h24v24H0z"
|
|
44
|
+
/>
|
|
45
|
+
</clipPath>
|
|
46
|
+
</defs>
|
|
47
|
+
</svg>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<script>
|
|
51
|
+
/**
|
|
52
|
+
* Animated SVG Loading Spinner with variable size and color.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
export default {
|
|
56
|
+
props: {
|
|
57
|
+
/**
|
|
58
|
+
* The size of the loading spinner.
|
|
59
|
+
* 'small', 'medium', or 'large'
|
|
60
|
+
* */
|
|
61
|
+
size: {
|
|
62
|
+
type: String,
|
|
63
|
+
default: 'medium',
|
|
64
|
+
validator(value) {
|
|
65
|
+
return ['small', 'medium', 'large'].includes(value);
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* The color of the loading spinner.
|
|
70
|
+
* 'brand', 'white', or 'black'
|
|
71
|
+
* */
|
|
72
|
+
color: {
|
|
73
|
+
type: String,
|
|
74
|
+
default: 'brand',
|
|
75
|
+
validator(value) {
|
|
76
|
+
return ['brand', 'white', 'black'].includes(value);
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
</script>
|