@kiva/kv-components 3.2.0 → 3.4.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/CHANGELOG.md +39 -0
- package/package.json +2 -2
- package/utils/expander.js +72 -0
- package/vue/KvAccordionItem.vue +130 -0
- package/vue/KvExpandable.vue +84 -0
- package/vue/KvLoadingPlaceholder.vue +38 -0
- package/vue/KvTextInput.vue +1 -0
- package/vue/stories/KvAccordionItem.stories.js +24 -0
- package/vue/stories/KvLoadingPlaceholder.stories.js +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,45 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [3.4.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.3.1...@kiva/kv-components@3.4.0) (2022-08-29)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* creating a global resuable component (KvAccordionItem) ([5bf7685](https://github.com/kiva/kv-ui-elements/commit/5bf76858508870f9055e680406a1167cdd68099c))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [3.3.1](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.3.0...@kiva/kv-components@3.3.1) (2022-08-26)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* ensure ref is defined in exported methods ([3ab00ea](https://github.com/kiva/kv-ui-elements/commit/3ab00ea181ee2455925c2f8f01950b79c2ea7b09))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# [3.3.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.2.0...@kiva/kv-components@3.3.0) (2022-08-10)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Bug Fixes
|
|
32
|
+
|
|
33
|
+
* added class to placeholder story to show support ([ed4e6c9](https://github.com/kiva/kv-ui-elements/commit/ed4e6c984b6a52b238862004266ada10c9cb0c95))
|
|
34
|
+
* added non-static class and style to placeholder as example ([4fd5e98](https://github.com/kiva/kv-ui-elements/commit/4fd5e98d41c900f69b56614980c688ebd43f6176))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Features
|
|
38
|
+
|
|
39
|
+
* placeholder component ([b29facc](https://github.com/kiva/kv-ui-elements/commit/b29facc8c8fcdb9761cc9e58e2530ea1e3b4a2f8))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
6
45
|
# [3.2.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.1.0...@kiva/kv-components@3.2.0) (2022-08-04)
|
|
7
46
|
|
|
8
47
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiva/kv-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"optional": true
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "fa419acdf33c3d416a926999f23e43d3cfa882b9"
|
|
72
72
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
|
|
3
|
+
function setInitialStyle(el, { property, delay, easing }) {
|
|
4
|
+
el.style.overflow = 'hidden';
|
|
5
|
+
el.style.transition = `${property} ${delay}ms ${easing}`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function unsetStyles(el, { property }) {
|
|
9
|
+
el.style[property] = null;
|
|
10
|
+
el.style.overflow = null;
|
|
11
|
+
el.style.transition = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function expand(el, {
|
|
15
|
+
easing = 'ease',
|
|
16
|
+
delay = 500,
|
|
17
|
+
done = () => {},
|
|
18
|
+
from = 0,
|
|
19
|
+
property = 'height',
|
|
20
|
+
}) {
|
|
21
|
+
// set initial styles
|
|
22
|
+
setInitialStyle(el, { property, delay, easing });
|
|
23
|
+
|
|
24
|
+
// need to measure the property, so first set the value to 'auto'
|
|
25
|
+
// then unset display:none from v-show
|
|
26
|
+
el.style[property] = 'auto';
|
|
27
|
+
el.style.display = null;
|
|
28
|
+
|
|
29
|
+
// measure the property
|
|
30
|
+
const propValue = window.getComputedStyle(el).getPropertyValue(property);
|
|
31
|
+
|
|
32
|
+
// set the property to the 'from' value
|
|
33
|
+
el.style[property] = from;
|
|
34
|
+
|
|
35
|
+
el.addEventListener('transitionend', function listener() {
|
|
36
|
+
unsetStyles(el, { property });
|
|
37
|
+
// finally, call the done callback after the transition
|
|
38
|
+
done();
|
|
39
|
+
el.removeEventListener('transitionend', listener, true);
|
|
40
|
+
}, true);
|
|
41
|
+
|
|
42
|
+
// hack to cause the browser to reflow
|
|
43
|
+
void el.offsetWidth; // eslint-disable-line no-void
|
|
44
|
+
// ...and set the property to the measured value on the next tick so it animates w/ css
|
|
45
|
+
el.style[property] = propValue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function collapse(el, {
|
|
49
|
+
easing = 'ease',
|
|
50
|
+
delay = 500,
|
|
51
|
+
done = () => {},
|
|
52
|
+
to = 0,
|
|
53
|
+
property = 'height',
|
|
54
|
+
}) {
|
|
55
|
+
// set initial styles
|
|
56
|
+
setInitialStyle(el, { property, delay, easing });
|
|
57
|
+
|
|
58
|
+
// explicitly set the property value...
|
|
59
|
+
el.style[property] = window.getComputedStyle(el).getPropertyValue(property);
|
|
60
|
+
|
|
61
|
+
el.addEventListener('transitionend', function listener() {
|
|
62
|
+
unsetStyles(el, { property });
|
|
63
|
+
// finally, call the done callback after the transition
|
|
64
|
+
done();
|
|
65
|
+
el.removeEventListener('transitionend', listener, true);
|
|
66
|
+
}, true);
|
|
67
|
+
|
|
68
|
+
// hack to cause the browser to reflow
|
|
69
|
+
void el.offsetWidth; // eslint-disable-line no-void
|
|
70
|
+
// ...and set the property to the 'to' value on the next tick so it animates w/ css
|
|
71
|
+
el.style[property] = to;
|
|
72
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="tw-border-b tw-border-tertiary tw-relative last:tw-border-b-0"
|
|
4
|
+
>
|
|
5
|
+
<button
|
|
6
|
+
class="tw-w-full tw-flex tw-justify-between tw-items-center tw-py-1.5 tw-px-0
|
|
7
|
+
tw-text-left disabled:tw-cursor-not-allowed disabled:tw-opacity-low
|
|
8
|
+
hover:tw-text-action-highlight focus:tw-text-action-highlight"
|
|
9
|
+
:disabled="disabled"
|
|
10
|
+
:aria-controls="`kv-accordion-${id}`"
|
|
11
|
+
:aria-expanded="isOpen ? 'true' : 'false'"
|
|
12
|
+
@click.prevent="toggle"
|
|
13
|
+
>
|
|
14
|
+
<span class="tw-flex-1">
|
|
15
|
+
<slot name="header"></slot>
|
|
16
|
+
</span>
|
|
17
|
+
<kv-material-icon
|
|
18
|
+
class="tw-h-3 tw-w-3 tw-transition tw-transform tw-duration-500 tw-ease"
|
|
19
|
+
:class="{ 'tw-rotate-180' : isOpen }"
|
|
20
|
+
:icon="mdiChevronDown"
|
|
21
|
+
/>
|
|
22
|
+
</button>
|
|
23
|
+
<kv-expandable>
|
|
24
|
+
<div
|
|
25
|
+
v-show="isOpen"
|
|
26
|
+
:id="`kv-accordion-${id}`"
|
|
27
|
+
:aria-hidden="isOpen ? 'false' : 'true'"
|
|
28
|
+
>
|
|
29
|
+
<slot></slot>
|
|
30
|
+
</div>
|
|
31
|
+
</kv-expandable>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
// Accordion a11y resources
|
|
37
|
+
// https://www.w3.org/TR/wai-aria-practices-1.1/examples/accordion/accordion.html
|
|
38
|
+
// https://www.aditus.io/patterns/accordion/
|
|
39
|
+
// Future improvement
|
|
40
|
+
// Currently the slot content is inside the button, which means h2, h3 etc. won't be
|
|
41
|
+
// navigatable via headings. See https://daverupert.com/2019/12/why-details-is-not-an-accordion/
|
|
42
|
+
// h2 + button // ✅ H1 will show up when navigating by headings
|
|
43
|
+
// button + h2 // ❌ H1 will not show up when navigating by headings
|
|
44
|
+
// Perhaps we could do some magic DOM reordering via this.$slots.header or
|
|
45
|
+
// pass a prop like 'tag' that sets the parent node of the button. <accordion tag="h3">...
|
|
46
|
+
|
|
47
|
+
import {
|
|
48
|
+
ref,
|
|
49
|
+
toRefs,
|
|
50
|
+
} from 'vue-demi';
|
|
51
|
+
import { mdiChevronDown } from '@mdi/js';
|
|
52
|
+
import KvExpandable from './KvExpandable.vue';
|
|
53
|
+
import KvMaterialIcon from './KvMaterialIcon.vue';
|
|
54
|
+
|
|
55
|
+
export default {
|
|
56
|
+
components: {
|
|
57
|
+
KvMaterialIcon,
|
|
58
|
+
KvExpandable,
|
|
59
|
+
},
|
|
60
|
+
props: {
|
|
61
|
+
/**
|
|
62
|
+
* Unique id. used for a11y
|
|
63
|
+
* */
|
|
64
|
+
id: {
|
|
65
|
+
type: String,
|
|
66
|
+
required: true,
|
|
67
|
+
validator: (v) => v.length > 0 && !/\s/g.test(v), // must be a valid html5 id
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Whether the body is shown initially
|
|
71
|
+
* */
|
|
72
|
+
open: {
|
|
73
|
+
type: Boolean,
|
|
74
|
+
default: false,
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* Whether the accordion can be toggled
|
|
78
|
+
* */
|
|
79
|
+
disabled: {
|
|
80
|
+
type: Boolean,
|
|
81
|
+
default: false,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
emits: [
|
|
85
|
+
'toggle',
|
|
86
|
+
],
|
|
87
|
+
setup(props, { emit }) {
|
|
88
|
+
const {
|
|
89
|
+
open,
|
|
90
|
+
disabled,
|
|
91
|
+
} = toRefs(props);
|
|
92
|
+
|
|
93
|
+
const isOpen = ref(open.value);
|
|
94
|
+
|
|
95
|
+
const toggle = () => {
|
|
96
|
+
if (!disabled.value) {
|
|
97
|
+
isOpen.value = !isOpen.value;
|
|
98
|
+
/**
|
|
99
|
+
* Fires when the accordion has been toggled.
|
|
100
|
+
* Contains an object with a boolean 'open' property of the current open
|
|
101
|
+
* state of the accordion
|
|
102
|
+
* @event toggle
|
|
103
|
+
* @type {Event}
|
|
104
|
+
*/
|
|
105
|
+
emit('toggle', { open: isOpen.value });
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const expand = () => {
|
|
110
|
+
if (!disabled.value) {
|
|
111
|
+
isOpen.value = true;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const collapse = () => {
|
|
116
|
+
if (!disabled.value) {
|
|
117
|
+
isOpen.value = false;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
collapse,
|
|
123
|
+
expand,
|
|
124
|
+
isOpen,
|
|
125
|
+
mdiChevronDown,
|
|
126
|
+
toggle,
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
</script>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<transition
|
|
3
|
+
@enter="enter"
|
|
4
|
+
@leave="leave"
|
|
5
|
+
>
|
|
6
|
+
<slot></slot>
|
|
7
|
+
</transition>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script>
|
|
11
|
+
import {
|
|
12
|
+
toRefs,
|
|
13
|
+
} from 'vue-demi';
|
|
14
|
+
import { expand, collapse } from '../utils/expander';
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
props: {
|
|
18
|
+
property: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: 'height',
|
|
21
|
+
},
|
|
22
|
+
delay: {
|
|
23
|
+
type: Number,
|
|
24
|
+
default: 500,
|
|
25
|
+
},
|
|
26
|
+
easing: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: 'ease',
|
|
29
|
+
},
|
|
30
|
+
skipEnter: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: false,
|
|
33
|
+
},
|
|
34
|
+
skipLeave: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
setup(props) {
|
|
40
|
+
const {
|
|
41
|
+
property,
|
|
42
|
+
delay,
|
|
43
|
+
easing,
|
|
44
|
+
skipEnter,
|
|
45
|
+
skipLeave,
|
|
46
|
+
} = toRefs(props);
|
|
47
|
+
|
|
48
|
+
const enter = (el, done) => {
|
|
49
|
+
if (skipEnter.value) {
|
|
50
|
+
return done();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
expand(el, {
|
|
54
|
+
property: property.value,
|
|
55
|
+
delay: delay.value,
|
|
56
|
+
easing: easing.value,
|
|
57
|
+
done,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return true;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const leave = (el, done) => {
|
|
64
|
+
if (skipLeave.value) {
|
|
65
|
+
return done();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
collapse(el, {
|
|
69
|
+
property: property.value,
|
|
70
|
+
delay: delay.value,
|
|
71
|
+
easing: easing.value,
|
|
72
|
+
done,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return true;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
enter,
|
|
80
|
+
leave,
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
</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>
|
package/vue/KvTextInput.vue
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import KvAccordionItem from '../KvAccordionItem.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'KvAccordionItem',
|
|
5
|
+
component: KvAccordionItem,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const DefaultTemplate = () => ({
|
|
9
|
+
components: { KvAccordionItem },
|
|
10
|
+
template: `
|
|
11
|
+
<div style="padding: 20px;">
|
|
12
|
+
<kv-accordion-item id="accordian-test">
|
|
13
|
+
<template #header>
|
|
14
|
+
<h2>Accordion</h2>
|
|
15
|
+
</template>
|
|
16
|
+
<p>
|
|
17
|
+
"Hello, KvAccordion Contents!"
|
|
18
|
+
</p>
|
|
19
|
+
</kv-accordion-item>
|
|
20
|
+
</div>
|
|
21
|
+
`,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const Default = DefaultTemplate.bind({});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import KvLoadingPlaceholder from '../KvLoadingPlaceholder.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'KvLoadingPlaceholder',
|
|
5
|
+
component: KvLoadingPlaceholder,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const DefaultTemplate = () => ({
|
|
9
|
+
components: { KvLoadingPlaceholder },
|
|
10
|
+
template: `
|
|
11
|
+
<div style="padding: 20px;">
|
|
12
|
+
<kv-loading-placeholder class="tw-py-1" :class="{ 'tw-px-1': true }" style="height: 50px;" :style="{ width: '400px' }" />
|
|
13
|
+
</div>
|
|
14
|
+
`,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const Default = DefaultTemplate.bind({});
|