@kiva/kv-components 3.3.0 → 3.5.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 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.5.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.4.0...@kiva/kv-components@3.5.0) (2022-09-07)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * height of indicator bar ([8908e44](https://github.com/kiva/kv-ui-elements/commit/8908e44da2d3edcfbc9bfca44144604ed255674b))
12
+
13
+
14
+ ### Features
15
+
16
+ * add vertical tab button for new home page exp ([329e490](https://github.com/kiva/kv-ui-elements/commit/329e490a8df555b126c23e93f4ce69cd00eb7c35))
17
+ * add vertical variant in tab ([a23c9d1](https://github.com/kiva/kv-ui-elements/commit/a23c9d1abd1ade6a2603d422bb0f2535e299d119))
18
+
19
+
20
+
21
+
22
+
23
+ # [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)
24
+
25
+
26
+ ### Features
27
+
28
+ * creating a global resuable component (KvAccordionItem) ([5bf7685](https://github.com/kiva/kv-ui-elements/commit/5bf76858508870f9055e680406a1167cdd68099c))
29
+
30
+
31
+
32
+
33
+
34
+ ## [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)
35
+
36
+
37
+ ### Bug Fixes
38
+
39
+ * ensure ref is defined in exported methods ([3ab00ea](https://github.com/kiva/kv-ui-elements/commit/3ab00ea181ee2455925c2f8f01950b79c2ea7b09))
40
+
41
+
42
+
43
+
44
+
6
45
  # [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)
7
46
 
8
47
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kiva/kv-components",
3
- "version": "3.3.0",
3
+ "version": "3.5.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": "a2d53f16ab9b5f1eeb174d372a7ac50bc9c9f691"
71
+ "gitHead": "09af63c2169df1b45e40de57569ce5b51b6141fc"
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>
package/vue/KvTab.vue CHANGED
@@ -1,8 +1,11 @@
1
1
  <template>
2
2
  <button
3
3
  :id="`kv-tab-${forPanel}`"
4
- class="tw-text-h3 tw-mb-1.5 tw-whitespace-nowrap"
5
- :class="{ 'hover:tw-text-action-highlight' : !isActive }"
4
+ class="tw-text-h3 tw-mb-1.5 tw-whitespace-nowrap tw-text-left"
5
+ :class="{ 'hover:tw-text-action-highlight' : !isActive,
6
+ 'md:tw-border-l-2 tw-border-transparent md:tw-pl-2' : vertical,
7
+ 'tw-text-action-highlight' : isActive && vertical
8
+ }"
6
9
  role="tab"
7
10
  :aria-selected="isActive"
8
11
  :aria-controls="`kv-tab-panel-${forPanel}`"
@@ -39,6 +42,10 @@ export default {
39
42
  type: Boolean,
40
43
  default: false,
41
44
  },
45
+ vertical: {
46
+ type: Boolean,
47
+ default: false,
48
+ },
42
49
  },
43
50
  setup(props) {
44
51
  const {
package/vue/KvTabs.vue CHANGED
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div>
2
+ <div :class="vertical ? 'md:tw-flex' : ''">
3
3
  <div
4
4
  role="tablist"
5
5
  class="
@@ -7,10 +7,13 @@
7
7
  tw-gap-x-2.5 md:tw-gap-x-5 lg:tw-gap-x-6
8
8
  tw-mb-3 lg:tw-mb-4
9
9
  "
10
+ :class="{'md:tw-flex-col md:tw-mr-3' : vertical}"
10
11
  @keydown="handleKeyDown($event)"
11
12
  >
12
13
  <!-- @slot Tab Navigation -->
13
- <slot name="tabNav"></slot>
14
+ <slot
15
+ name="tabNav"
16
+ ></slot>
14
17
 
15
18
  <!-- indicator bar -->
16
19
  <div
@@ -19,9 +22,12 @@
19
22
  tw-bg-primary-inverse tw-rounded-full
20
23
  tw-origin-left tw-transition-all tw-duration-300
21
24
  "
25
+ :class="{ 'tw-hidden md:tw-block tw-top-0 md:tw-bg-action-highlight' : vertical}"
22
26
  :style="`
23
- width: ${selectedTabEl ? selectedTabEl.clientWidth : 0}px;
24
- transform: ${selectedTabEl ? `translateX(${selectedTabEl.offsetLeft}px)` : null};
27
+ width: ${selectedTabEl && !vertical ? selectedTabEl.clientWidth : 3}px;
28
+ height: ${selectedTabEl && vertical ? `${selectedTabEl.clientHeight}px` : '0.25rem'};
29
+ transform: ${selectedTabEl && !vertical ? `translateX(${selectedTabEl.offsetLeft}px)`
30
+ : selectedTabEl ? `translateY(${selectedTabEl.offsetTop}px)` : null};
25
31
  `"
26
32
  ></div>
27
33
  </div>
@@ -65,6 +71,12 @@ import {
65
71
  } from 'vue-demi';
66
72
 
67
73
  export default {
74
+ props: {
75
+ vertical: {
76
+ type: Boolean,
77
+ default: false,
78
+ },
79
+ },
68
80
  setup(props, { emit }) {
69
81
  const tabContext = reactive({
70
82
  selectedIndex: 0,
@@ -240,6 +240,7 @@ export default {
240
240
  styles,
241
241
  inputAttrs,
242
242
  inputListeners,
243
+ textInputRef,
243
244
  };
244
245
  },
245
246
  };
@@ -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({});
@@ -5,6 +5,9 @@ import KvTabPanel from '../KvTabPanel.vue';
5
5
  export default {
6
6
  title: 'KvTabs',
7
7
  component: KvTabs,
8
+ args: {
9
+ vertical: true,
10
+ },
8
11
  };
9
12
 
10
13
  export const DefaultTemplate = () => ({
@@ -57,6 +60,32 @@ export const InitialTabSelection = () => ({
57
60
  },
58
61
  });
59
62
 
63
+ export const VerticalOrientation = (args, { argTypes }) => ({
64
+ props: Object.keys(argTypes),
65
+ components: { KvTabs, KvTab, KvTabPanel },
66
+ template: `
67
+ <kv-tabs @tab-changed="handleTabChanged" :vertical="vertical">
68
+ <template #tabNav>
69
+ <kv-tab :vertical="vertical" forPanel="demo-1-first">First</kv-tab>
70
+ <kv-tab :vertical="vertical" forPanel="demo-1-second">Second</kv-tab>
71
+ <kv-tab :vertical="vertical" forPanel="demo-1-third">Third</kv-tab>
72
+ <kv-tab :vertical="vertical" forPanel="demo-1-forth">Forth is longer</kv-tab>
73
+ </template>
74
+ <template #tabPanels>
75
+ <kv-tab-panel id="demo-1-first"><p>First Panel</p></kv-tab-panel>
76
+ <kv-tab-panel id="demo-1-second"><p>Second Panel has <br>longer<br>content</p></kv-tab-panel>
77
+ <kv-tab-panel id="demo-1-third"><p>Third Panel</p></kv-tab-panel>
78
+ <kv-tab-panel id="demo-1-forth"><p>Forth Panel</p></kv-tab-panel>
79
+ </template>
80
+ </kv-tabs>
81
+ `,
82
+ methods: {
83
+ handleTabChanged(index) {
84
+ console.log(index);
85
+ },
86
+ },
87
+ });
88
+
60
89
  export const MultipleOnAPage = () => ({
61
90
  components: { KvTabs, KvTab, KvTabPanel },
62
91
  template: `