@kiva/kv-components 2.0.0 → 3.0.0
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/{.eslintrc.js → .eslintrc.cjs} +1 -1
- package/CHANGELOG.md +45 -0
- package/__mocks__/ResizeObserver.js +13 -0
- package/package.json +23 -10
- package/{postcss.config.js → postcss.config.cjs} +2 -2
- package/{tailwind.config.js → tailwind.config.cjs} +3 -3
- package/tests/unit/jest-setup.js +5 -0
- package/tests/unit/specs/components/KvButton.spec.js +14 -25
- package/tests/unit/specs/components/KvCarousel.spec.js +11 -0
- package/tests/unit/specs/components/KvCheckbox.spec.js +73 -14
- package/tests/unit/specs/components/KvLightbox.spec.js +14 -0
- package/tests/unit/specs/components/KvProgressBar.spec.js +11 -0
- package/tests/unit/specs/components/KvRadio.spec.js +94 -5
- package/tests/unit/specs/components/KvSelect.spec.js +113 -0
- package/tests/unit/specs/components/KvSwitch.spec.js +92 -33
- package/tests/unit/specs/components/KvTabPanel.spec.js +11 -0
- package/tests/unit/specs/components/KvTabs.spec.js +99 -0
- package/tests/unit/specs/components/KvTextInput.spec.js +86 -9
- package/tests/unit/specs/components/KvTextLink.spec.js +16 -24
- package/tests/unit/specs/components/KvToast.spec.js +11 -0
- package/tests/unit/utils/addVueRouter.js +24 -0
- package/utils/attrs.js +62 -0
- package/utils/{themeUtils.js → themeUtils.cjs} +0 -0
- package/vue/.storybook/{main.js → main.cjs} +13 -5
- package/vue/.storybook/preview.js +6 -1
- package/vue/KvButton.vue +80 -53
- package/vue/KvCarousel.vue +142 -106
- package/vue/KvCheckbox.vue +86 -60
- package/vue/KvContentfulImg.vue +45 -34
- package/vue/KvLightbox.vue +108 -69
- package/vue/KvProgressBar.vue +33 -19
- package/vue/KvRadio.vue +72 -41
- package/vue/KvSelect.vue +46 -20
- package/vue/KvSwitch.vue +55 -33
- package/vue/KvTab.vue +49 -21
- package/vue/KvTabPanel.vue +26 -6
- package/vue/KvTabs.vue +70 -53
- package/vue/KvTextInput.vue +71 -48
- package/vue/KvTextLink.vue +42 -20
- package/vue/KvThemeProvider.vue +1 -1
- package/vue/KvToast.vue +53 -37
- package/vue/stories/KvCheckbox.stories.js +5 -5
- package/vue/stories/KvSwitch.stories.js +2 -2
- package/vue/stories/KvTabs.stories.js +8 -8
- package/vue/stories/KvTextInput.stories.js +1 -1
- package/vue/stories/KvThemeProvider.stories.js +1 -1
- package/vue/stories/KvToast.stories.js +3 -2
- package/vue/stories/StyleguidePrimitives.stories.js +9 -9
- package/.babelrc +0 -16
- package/jest.config.js +0 -36
package/vue/KvCheckbox.vue
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
:class="classes"
|
|
4
|
+
:style="styles"
|
|
5
|
+
>
|
|
3
6
|
<label
|
|
4
7
|
class="tw-inline-flex tw-items-center"
|
|
5
8
|
:class="{ 'tw-opacity-low': disabled }"
|
|
6
9
|
:for="uuid"
|
|
7
10
|
>
|
|
8
11
|
<input
|
|
9
|
-
v-bind="
|
|
12
|
+
v-bind="inputAttrs"
|
|
10
13
|
:id="uuid"
|
|
11
14
|
ref="checkboxRef"
|
|
12
15
|
class="tw-peer tw-appearance-none tw-w-max"
|
|
@@ -61,6 +64,18 @@
|
|
|
61
64
|
|
|
62
65
|
<script>
|
|
63
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
|
+
];
|
|
64
79
|
|
|
65
80
|
/**
|
|
66
81
|
* Use as you would an <input type="checkbox" />
|
|
@@ -69,16 +84,17 @@ import { nanoid } from 'nanoid';
|
|
|
69
84
|
|
|
70
85
|
export default {
|
|
71
86
|
inheritAttrs: false,
|
|
72
|
-
|
|
87
|
+
|
|
73
88
|
model: {
|
|
74
|
-
prop: '
|
|
75
|
-
event: '
|
|
89
|
+
prop: 'modelValue',
|
|
90
|
+
event: 'update:modelValue',
|
|
76
91
|
},
|
|
92
|
+
|
|
77
93
|
props: {
|
|
78
94
|
/**
|
|
79
95
|
* Whether the checkbox is checked or not
|
|
80
96
|
* */
|
|
81
|
-
|
|
97
|
+
modelValue: {
|
|
82
98
|
type: [Boolean, Array],
|
|
83
99
|
default: false,
|
|
84
100
|
},
|
|
@@ -93,7 +109,7 @@ export default {
|
|
|
93
109
|
* Value of the checkbox if v-model is an array
|
|
94
110
|
* */
|
|
95
111
|
value: {
|
|
96
|
-
type: String,
|
|
112
|
+
type: [String, Boolean],
|
|
97
113
|
default: '',
|
|
98
114
|
},
|
|
99
115
|
/**
|
|
@@ -105,73 +121,83 @@ export default {
|
|
|
105
121
|
default: true,
|
|
106
122
|
},
|
|
107
123
|
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
computed: {
|
|
115
|
-
inputListeners() {
|
|
116
|
-
return {
|
|
117
|
-
// Pass through any listeners from the parent to the input element, like blur, focus, etc.
|
|
118
|
-
// https://vuejs.org/v2/guide/components-custom-events.html#Binding-Native-Events-to-Components
|
|
119
|
-
...this.$listeners,
|
|
120
|
-
// ...except for the listener to the 'change' event which is emitted by this component
|
|
121
|
-
change: () => {},
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
watch: {
|
|
126
|
-
checked() {
|
|
127
|
-
this.setChecked();
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
mounted() {
|
|
131
|
-
this.uuid = `kvc-${nanoid(10)}`;
|
|
132
|
-
},
|
|
133
|
-
created() {
|
|
134
|
-
this.setChecked();
|
|
135
|
-
},
|
|
136
|
-
methods: {
|
|
137
|
-
onChange(event) {
|
|
138
|
-
// get the input[type=checkbox] state
|
|
139
|
-
const isChecked = event.target.checked;
|
|
124
|
+
emits,
|
|
125
|
+
setup(props, context) {
|
|
126
|
+
const {
|
|
127
|
+
modelValue,
|
|
128
|
+
value,
|
|
129
|
+
} = toRefs(props);
|
|
140
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;
|
|
141
145
|
let checkboxValue;
|
|
142
146
|
|
|
143
|
-
if (Array.isArray(
|
|
147
|
+
if (Array.isArray(modelValue.value)) {
|
|
144
148
|
// if the model is an array, add or remove our value from it
|
|
145
|
-
if (
|
|
146
|
-
checkboxValue = [...
|
|
149
|
+
if (inputChecked) {
|
|
150
|
+
checkboxValue = [...modelValue.value, event.target.value];
|
|
147
151
|
} else {
|
|
148
|
-
checkboxValue =
|
|
152
|
+
checkboxValue = modelValue.value.filter((item) => item !== value.value);
|
|
149
153
|
}
|
|
150
154
|
} else {
|
|
151
|
-
checkboxValue =
|
|
155
|
+
checkboxValue = inputChecked;
|
|
152
156
|
}
|
|
153
157
|
|
|
154
158
|
// emit the change event to update the model
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (Array.isArray(this.checked)) {
|
|
159
|
+
emit('change', checkboxValue);
|
|
160
|
+
emit('update:modelValue', checkboxValue);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const setChecked = () => {
|
|
164
|
+
if (Array.isArray(modelValue.value)) {
|
|
162
165
|
// if the model is array like <kv-checkbox v-model="['item1', 'item2']" value="item1">
|
|
163
|
-
|
|
166
|
+
isChecked.value = modelValue.value.includes(value.value);
|
|
164
167
|
} else {
|
|
165
168
|
// else it's a boolean like <kv-checkbox v-model="true">
|
|
166
|
-
|
|
169
|
+
isChecked.value = modelValue.value;
|
|
167
170
|
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
+
};
|
|
175
200
|
},
|
|
176
201
|
};
|
|
202
|
+
|
|
177
203
|
</script>
|
package/vue/KvContentfulImg.vue
CHANGED
|
@@ -5,29 +5,29 @@
|
|
|
5
5
|
>
|
|
6
6
|
<!-- Set of image sources -->
|
|
7
7
|
<template v-if="sourceSizes.length > 0">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
8
|
+
<!-- browser supports webp -->
|
|
9
|
+
<source
|
|
10
|
+
v-for="(image, index) in sourceSizes"
|
|
11
|
+
:key="'webp-image'+index"
|
|
12
|
+
:media="'('+image.media+')'"
|
|
13
|
+
type="image/webp"
|
|
14
|
+
:width="image.width ? image.width : null"
|
|
15
|
+
:height="image.height ? image.height : null"
|
|
16
|
+
:srcset="`
|
|
17
|
+
${buildUrl(image, 2)}&fit=${fit}&f=${focus}&fm=webp&q=65 2x,
|
|
18
|
+
${buildUrl(image)}&fit=${fit}&f=${focus}&fm=webp&q=80 1x`"
|
|
19
|
+
>
|
|
20
|
+
<!-- browser doesn't support webp -->
|
|
21
|
+
<source
|
|
22
|
+
v-for="(image, index) in sourceSizes"
|
|
23
|
+
:key="'fallback-image'+index"
|
|
24
|
+
:media="'('+image.media+')'"
|
|
25
|
+
:width="image.width ? image.width : null"
|
|
26
|
+
:height="image.height ? image.height : null"
|
|
27
|
+
:srcset="`
|
|
28
|
+
${buildUrl(image, 2)}&fit=${fit}&f=${focus}&fm=${fallbackFormat}&q=65 2x,
|
|
29
|
+
${buildUrl(image)}&fit=${fit}&f=${focus}&fm=${fallbackFormat}&q=80 1x`"
|
|
30
|
+
>
|
|
31
31
|
<!-- browser doesn't support picture element -->
|
|
32
32
|
<img
|
|
33
33
|
class="tw-max-w-full tw-max-h-full"
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
</template>
|
|
66
66
|
|
|
67
67
|
<script>
|
|
68
|
+
import { toRefs } from 'vue-demi';
|
|
68
69
|
// Since it's easy for marketing or other to upload massive images to contentful,
|
|
69
70
|
// in order to be performant respectful of our users data plans, and not damage
|
|
70
71
|
// our SEO, we shouldn't send the source image directly to our users.
|
|
@@ -172,23 +173,33 @@ export default {
|
|
|
172
173
|
default: () => [],
|
|
173
174
|
},
|
|
174
175
|
},
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
setup(props) {
|
|
177
|
+
const {
|
|
178
|
+
contentfulSrc,
|
|
179
|
+
width,
|
|
180
|
+
height,
|
|
181
|
+
} = toRefs(props);
|
|
182
|
+
|
|
183
|
+
const buildUrl = (image = null, multiplier = 1) => {
|
|
184
|
+
let src = image && image.url ? `${image.url}?` : `${contentfulSrc.value}?`;
|
|
185
|
+
const imgWidth = image ? image.width : width.value;
|
|
186
|
+
const imgHeight = image ? image.height : height.value;
|
|
180
187
|
|
|
181
|
-
if (
|
|
182
|
-
src += `w=${
|
|
188
|
+
if (imgWidth) {
|
|
189
|
+
src += `w=${imgWidth * multiplier}`;
|
|
183
190
|
}
|
|
184
|
-
if (
|
|
191
|
+
if (imgWidth && imgHeight) {
|
|
185
192
|
src += '&';
|
|
186
193
|
}
|
|
187
|
-
if (
|
|
188
|
-
src += `h=${
|
|
194
|
+
if (imgHeight) {
|
|
195
|
+
src += `h=${imgHeight * multiplier}`;
|
|
189
196
|
}
|
|
190
197
|
return src;
|
|
191
|
-
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
buildUrl,
|
|
202
|
+
};
|
|
192
203
|
},
|
|
193
204
|
};
|
|
194
205
|
</script>
|
package/vue/KvLightbox.vue
CHANGED
|
@@ -19,10 +19,7 @@
|
|
|
19
19
|
"
|
|
20
20
|
@click.stop.prevent="onScreenClick"
|
|
21
21
|
>
|
|
22
|
-
<
|
|
23
|
-
:disabled="!visible"
|
|
24
|
-
:return-focus="true"
|
|
25
|
-
>
|
|
22
|
+
<div>
|
|
26
23
|
<div
|
|
27
24
|
class="
|
|
28
25
|
tw-flex
|
|
@@ -82,7 +79,7 @@
|
|
|
82
79
|
tw-w-6 tw-h-6 tw--m-2
|
|
83
80
|
hover:tw-text-action-highlight
|
|
84
81
|
"
|
|
85
|
-
@click.stop
|
|
82
|
+
@click.stop="hide"
|
|
86
83
|
>
|
|
87
84
|
<kv-material-icon
|
|
88
85
|
class="tw-w-3 tw-h-3"
|
|
@@ -121,15 +118,23 @@
|
|
|
121
118
|
</div>
|
|
122
119
|
</div>
|
|
123
120
|
</div>
|
|
124
|
-
</
|
|
121
|
+
</div>
|
|
125
122
|
</div>
|
|
126
123
|
</transition>
|
|
127
124
|
</template>
|
|
128
125
|
|
|
129
126
|
<script>
|
|
130
|
-
|
|
127
|
+
import {
|
|
128
|
+
ref,
|
|
129
|
+
toRefs,
|
|
130
|
+
computed,
|
|
131
|
+
nextTick,
|
|
132
|
+
watch,
|
|
133
|
+
onBeforeUnmount,
|
|
134
|
+
onMounted,
|
|
135
|
+
} from 'vue-demi';
|
|
131
136
|
import { mdiClose } from '@mdi/js';
|
|
132
|
-
import
|
|
137
|
+
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap';
|
|
133
138
|
import { hideOthers as makePageInert } from 'aria-hidden';
|
|
134
139
|
import { lockScroll, unlockScroll } from '../utils/scrollLock';
|
|
135
140
|
import { lockPrintSingleEl, unlockPrintSingleEl } from '../utils/printing';
|
|
@@ -161,7 +166,6 @@ import KvMaterialIcon from './KvMaterialIcon.vue';
|
|
|
161
166
|
|
|
162
167
|
export default {
|
|
163
168
|
components: {
|
|
164
|
-
FocusLock,
|
|
165
169
|
KvMaterialIcon,
|
|
166
170
|
},
|
|
167
171
|
props: {
|
|
@@ -200,84 +204,119 @@ export default {
|
|
|
200
204
|
default: false,
|
|
201
205
|
},
|
|
202
206
|
},
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
+
|
|
221
|
+
const {
|
|
222
|
+
activate: activateFocusTrap,
|
|
223
|
+
deactivate: deactivateFocusTrap,
|
|
224
|
+
} = useFocusTrap(kvLightbox);
|
|
225
|
+
|
|
226
|
+
let makePageInertCallback = null;
|
|
227
|
+
let onKeyUp = null;
|
|
228
|
+
|
|
229
|
+
const role = computed(() => {
|
|
230
|
+
if (variant.value === 'alert') {
|
|
212
231
|
return 'alertdialog';
|
|
213
232
|
}
|
|
214
233
|
return 'dialog';
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const hide = () => {
|
|
237
|
+
// scroll any content inside the lightbox back to top
|
|
238
|
+
if (kvLightbox.value && kvLightboxBody.value) {
|
|
239
|
+
deactivateFocusTrap();
|
|
240
|
+
kvLightboxBody.value.scrollTop = 0;
|
|
241
|
+
unlockPrintSingleEl(kvLightboxBody.value);
|
|
223
242
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
243
|
+
unlockScroll();
|
|
244
|
+
if (makePageInertCallback) {
|
|
245
|
+
makePageInertCallback();
|
|
246
|
+
makePageInertCallback = null;
|
|
247
|
+
}
|
|
248
|
+
document.removeEventListener('keyup', onKeyUp);
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Triggered when the lightbox is closed
|
|
252
|
+
* @event lightbox-closed
|
|
253
|
+
* @type {Event}
|
|
254
|
+
*/
|
|
255
|
+
emit('lightbox-closed');
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
onKeyUp = (e) => {
|
|
259
|
+
if (!!e && e.key === 'Escape' && !preventClose.value) {
|
|
260
|
+
hide();
|
|
261
|
+
}
|
|
262
|
+
};
|
|
233
263
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
264
|
+
const onScreenClick = () => {
|
|
265
|
+
if (!preventClose.value) {
|
|
266
|
+
hide();
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const show = () => {
|
|
271
|
+
if (visible.value) {
|
|
272
|
+
document.addEventListener('keyup', onKeyUp);
|
|
273
|
+
|
|
274
|
+
nextTick(() => {
|
|
275
|
+
if (kvLightbox.value && kvLightboxBody.value) {
|
|
276
|
+
activateFocusTrap();
|
|
277
|
+
makePageInertCallback = makePageInert(kvLightbox.value);
|
|
278
|
+
lockPrintSingleEl(kvLightboxBody.value);
|
|
239
279
|
}
|
|
240
280
|
lockScroll();
|
|
241
281
|
|
|
242
282
|
// alerts should send focus to the first actionable item in the controls
|
|
243
|
-
if (
|
|
244
|
-
const firstControlEl =
|
|
283
|
+
if (variant.value === 'alert') {
|
|
284
|
+
const firstControlEl = controlsRef.value.querySelector('button');
|
|
245
285
|
if (firstControlEl) {
|
|
246
286
|
firstControlEl.focus();
|
|
247
287
|
}
|
|
248
288
|
}
|
|
249
289
|
});
|
|
250
290
|
}
|
|
251
|
-
}
|
|
252
|
-
hide() {
|
|
253
|
-
// scroll any content inside the lightbox back to top
|
|
254
|
-
const lightboxBodyRef = this.$refs.kvLightboxBody;
|
|
255
|
-
if (lightboxBodyRef) {
|
|
256
|
-
lightboxBodyRef.scrollTop = 0;
|
|
257
|
-
unlockPrintSingleEl(lightboxBodyRef);
|
|
258
|
-
}
|
|
259
|
-
unlockScroll();
|
|
260
|
-
if (this.makePageInert) {
|
|
261
|
-
this.makePageInert();
|
|
262
|
-
}
|
|
291
|
+
};
|
|
263
292
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
this.$emit('lightbox-closed');
|
|
270
|
-
},
|
|
271
|
-
onScreenClick() {
|
|
272
|
-
if (!this.preventClose) {
|
|
273
|
-
this.hide();
|
|
293
|
+
watch(visible, () => {
|
|
294
|
+
if (visible.value) {
|
|
295
|
+
show();
|
|
296
|
+
} else {
|
|
297
|
+
hide();
|
|
274
298
|
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
onMounted(() => {
|
|
302
|
+
if (visible.value) {
|
|
303
|
+
show();
|
|
279
304
|
}
|
|
280
|
-
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
onBeforeUnmount(() => hide());
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
mdiClose,
|
|
311
|
+
role,
|
|
312
|
+
kvLightbox,
|
|
313
|
+
kvLightboxBody,
|
|
314
|
+
onKeyUp,
|
|
315
|
+
onScreenClick,
|
|
316
|
+
hide,
|
|
317
|
+
show,
|
|
318
|
+
controlsRef,
|
|
319
|
+
};
|
|
281
320
|
},
|
|
282
321
|
};
|
|
283
322
|
</script>
|
package/vue/KvProgressBar.vue
CHANGED
|
@@ -25,6 +25,13 @@
|
|
|
25
25
|
/**
|
|
26
26
|
* Horizontal progress bar which communicates to the user the progress of a particular process
|
|
27
27
|
*/
|
|
28
|
+
import {
|
|
29
|
+
ref,
|
|
30
|
+
toRefs,
|
|
31
|
+
computed,
|
|
32
|
+
onMounted,
|
|
33
|
+
nextTick,
|
|
34
|
+
} from 'vue-demi';
|
|
28
35
|
|
|
29
36
|
export default {
|
|
30
37
|
props: {
|
|
@@ -59,28 +66,35 @@ export default {
|
|
|
59
66
|
required: true,
|
|
60
67
|
},
|
|
61
68
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
setup(props) {
|
|
70
|
+
const {
|
|
71
|
+
min,
|
|
72
|
+
max,
|
|
73
|
+
value,
|
|
74
|
+
} = toRefs(props);
|
|
75
|
+
const loaded = ref(false);
|
|
76
|
+
|
|
77
|
+
const percent = computed(() => {
|
|
78
|
+
const percentValue = ((value.value - min.value) / (max.value - min.value)) * 100;
|
|
79
|
+
const rounded = Math.round(percentValue * 10) / 10; // Keep percents to 1 decimal places 12.3%
|
|
71
80
|
const clamped = Math.min(Math.max(rounded, 0), 100); // Always between 0 and 100%
|
|
72
81
|
return clamped;
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
mounted() {
|
|
76
|
-
this.$nextTick(() => {
|
|
77
|
-
this.animateProgressBar();
|
|
78
82
|
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
|
|
84
|
+
const animateProgressBar = () => {
|
|
85
|
+
loaded.value = true;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
onMounted(async () => {
|
|
89
|
+
await nextTick();
|
|
90
|
+
animateProgressBar();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
loaded,
|
|
95
|
+
percent,
|
|
96
|
+
animateProgressBar,
|
|
97
|
+
};
|
|
84
98
|
},
|
|
85
99
|
};
|
|
86
100
|
</script>
|