@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.
Files changed (50) hide show
  1. package/{.eslintrc.js → .eslintrc.cjs} +1 -1
  2. package/CHANGELOG.md +45 -0
  3. package/__mocks__/ResizeObserver.js +13 -0
  4. package/package.json +23 -10
  5. package/{postcss.config.js → postcss.config.cjs} +2 -2
  6. package/{tailwind.config.js → tailwind.config.cjs} +3 -3
  7. package/tests/unit/jest-setup.js +5 -0
  8. package/tests/unit/specs/components/KvButton.spec.js +14 -25
  9. package/tests/unit/specs/components/KvCarousel.spec.js +11 -0
  10. package/tests/unit/specs/components/KvCheckbox.spec.js +73 -14
  11. package/tests/unit/specs/components/KvLightbox.spec.js +14 -0
  12. package/tests/unit/specs/components/KvProgressBar.spec.js +11 -0
  13. package/tests/unit/specs/components/KvRadio.spec.js +94 -5
  14. package/tests/unit/specs/components/KvSelect.spec.js +113 -0
  15. package/tests/unit/specs/components/KvSwitch.spec.js +92 -33
  16. package/tests/unit/specs/components/KvTabPanel.spec.js +11 -0
  17. package/tests/unit/specs/components/KvTabs.spec.js +99 -0
  18. package/tests/unit/specs/components/KvTextInput.spec.js +86 -9
  19. package/tests/unit/specs/components/KvTextLink.spec.js +16 -24
  20. package/tests/unit/specs/components/KvToast.spec.js +11 -0
  21. package/tests/unit/utils/addVueRouter.js +24 -0
  22. package/utils/attrs.js +62 -0
  23. package/utils/{themeUtils.js → themeUtils.cjs} +0 -0
  24. package/vue/.storybook/{main.js → main.cjs} +13 -5
  25. package/vue/.storybook/preview.js +6 -1
  26. package/vue/KvButton.vue +80 -53
  27. package/vue/KvCarousel.vue +142 -106
  28. package/vue/KvCheckbox.vue +86 -60
  29. package/vue/KvContentfulImg.vue +45 -34
  30. package/vue/KvLightbox.vue +108 -69
  31. package/vue/KvProgressBar.vue +33 -19
  32. package/vue/KvRadio.vue +72 -41
  33. package/vue/KvSelect.vue +46 -20
  34. package/vue/KvSwitch.vue +55 -33
  35. package/vue/KvTab.vue +49 -21
  36. package/vue/KvTabPanel.vue +26 -6
  37. package/vue/KvTabs.vue +70 -53
  38. package/vue/KvTextInput.vue +71 -48
  39. package/vue/KvTextLink.vue +42 -20
  40. package/vue/KvThemeProvider.vue +1 -1
  41. package/vue/KvToast.vue +53 -37
  42. package/vue/stories/KvCheckbox.stories.js +5 -5
  43. package/vue/stories/KvSwitch.stories.js +2 -2
  44. package/vue/stories/KvTabs.stories.js +8 -8
  45. package/vue/stories/KvTextInput.stories.js +1 -1
  46. package/vue/stories/KvThemeProvider.stories.js +1 -1
  47. package/vue/stories/KvToast.stories.js +3 -2
  48. package/vue/stories/StyleguidePrimitives.stories.js +9 -9
  49. package/.babelrc +0 -16
  50. package/jest.config.js +0 -36
package/vue/KvRadio.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 tw-align-middle"
5
8
  :class="{ 'tw-opacity-low': disabled }"
6
9
  :for="uuid"
7
10
  >
8
11
  <input
9
- v-bind="$attrs"
12
+ v-bind="inputAttrs"
10
13
  :id="uuid"
11
14
  ref="radioRef"
12
15
  class="tw-peer tw-appearance-none tw-w-max"
@@ -50,6 +53,18 @@
50
53
 
51
54
  <script>
52
55
  import { nanoid } from 'nanoid';
56
+ import {
57
+ computed,
58
+ onMounted,
59
+ ref,
60
+ toRefs,
61
+ } from 'vue-demi';
62
+ import { useAttrs } from '../utils/attrs';
63
+
64
+ const emits = [
65
+ 'change',
66
+ 'update:modelValue',
67
+ ];
53
68
 
54
69
  /* eslint-disable max-len */
55
70
 
@@ -79,10 +94,9 @@ import { nanoid } from 'nanoid';
79
94
 
80
95
  export default {
81
96
  inheritAttrs: false,
82
- // v-model will update when the checked radio changes
83
97
  model: {
84
- prop: 'checked',
85
- event: 'change',
98
+ prop: 'modelValue',
99
+ event: 'update:modelValue',
86
100
  },
87
101
  props: {
88
102
  /**
@@ -123,45 +137,62 @@ export default {
123
137
  type: [String, Number, Boolean],
124
138
  default: false,
125
139
  },
140
+ modelValue: {
141
+ type: [String, Number, Boolean],
142
+ default: false,
143
+ },
126
144
  },
127
- data() {
145
+ emits,
146
+ setup(props, context) {
147
+ const { emit } = context;
148
+ const {
149
+ value,
150
+ checked,
151
+ modelValue,
152
+ } = toRefs(props);
153
+
154
+ let uuid = ref(`kvr-${nanoid(10)}`);
155
+ const radioRef = ref(null);
156
+
157
+ const {
158
+ classes,
159
+ styles,
160
+ inputAttrs,
161
+ inputListeners,
162
+ } = useAttrs(context, emits);
163
+
164
+ const isChecked = computed(() => {
165
+ if (typeof modelValue.value === typeof value.value) {
166
+ return modelValue.value === value.value;
167
+ }
168
+ return Boolean(checked.value);
169
+ });
170
+
171
+ onMounted(() => {
172
+ uuid = `kvr-${nanoid(10)}`;
173
+ });
174
+
175
+ const onChange = (event) => {
176
+ emit('change', event.target.value);
177
+ emit('update:modelValue', event.target.value);
178
+ };
179
+
180
+ const focus = () => radioRef.focus();
181
+
182
+ const blur = () => radioRef.blur();
183
+
128
184
  return {
129
- uuid: `kvr-${nanoid(10)}`,
185
+ uuid,
186
+ isChecked,
187
+ radioRef,
188
+ onChange,
189
+ focus,
190
+ blur,
191
+ classes,
192
+ styles,
193
+ inputAttrs,
194
+ inputListeners,
130
195
  };
131
196
  },
132
- computed: {
133
- isChecked() {
134
- if (typeof this.checked === typeof this.value) {
135
- return this.checked === this.value;
136
- }
137
- return Boolean(this.checked);
138
- },
139
- inputListeners() {
140
- return {
141
- // Pass through any listeners from the parent to the input element, like blur, focus, etc.
142
- // https://vuejs.org/v2/guide/components-custom-events.html#Binding-Native-Events-to-Components
143
- ...this.$listeners,
144
- // ...except for the listener to the 'change' event which is emitted by this component
145
- change: () => {},
146
- };
147
- },
148
- },
149
- mounted() {
150
- this.uuid = `kvr-${nanoid(10)}`;
151
- },
152
- methods: {
153
- onChange(event) {
154
- /**
155
- * triggers when the value changes
156
- */
157
- this.$emit('change', event.target.value);
158
- },
159
- focus() {
160
- this.$refs.radioRef.focus();
161
- },
162
- blur() {
163
- this.$refs.radioRef.blur();
164
- },
165
- },
166
197
  };
167
198
  </script>
package/vue/KvSelect.vue CHANGED
@@ -1,11 +1,16 @@
1
1
  <template>
2
- <div class="tw-inline-flex">
2
+ <div
3
+ class="tw-inline-flex"
4
+ :class="classes"
5
+ :style="styles"
6
+ >
3
7
  <div class="tw-relative tw-w-full">
4
8
  <!-- eslint-disable max-len -->
5
9
  <select
6
10
  :id="id"
11
+ v-bind="inputAttrs"
7
12
  :disabled="disabled"
8
- :value="value"
13
+ :value="modelValue"
9
14
  class="tw-text-base tw-bg-primary tw-h-6 tw-pr-4 tw-pl-2 tw-border tw-border-tertiary tw-rounded-sm tw-appearance-none tw-w-full tw-ring-inset focus:tw-outline-none focus:tw-ring-2 focus:tw-ring-action focus:tw-border-transparent"
10
15
  :class="{ 'tw-opacity-low': disabled }"
11
16
  @change="onChange"
@@ -23,8 +28,15 @@
23
28
  </template>
24
29
 
25
30
  <script>
31
+ import 'vue-demi';
26
32
  import { mdiChevronDown } from '@mdi/js';
27
33
  import KvMaterialIcon from './KvMaterialIcon.vue';
34
+ import { useAttrs } from '../utils/attrs';
35
+
36
+ const emits = [
37
+ 'change',
38
+ 'update:modelValue',
39
+ ];
28
40
 
29
41
  export default {
30
42
  components: {
@@ -32,8 +44,8 @@ export default {
32
44
  },
33
45
  // v-model will change when select value updates
34
46
  model: {
35
- prop: 'value',
36
- event: 'change',
47
+ prop: 'modelValue',
48
+ event: 'update:modelValue',
37
49
  },
38
50
  props: {
39
51
  /**
@@ -44,13 +56,6 @@ export default {
44
56
  required: true,
45
57
  default: '',
46
58
  },
47
- /**
48
- * Initial selected value
49
- * */
50
- value: {
51
- type: [Number, String],
52
- default: 0,
53
- },
54
59
  /**
55
60
  * Use if select is disabled
56
61
  * */
@@ -58,21 +63,42 @@ export default {
58
63
  type: Boolean,
59
64
  default: false,
60
65
  },
66
+ /**
67
+ * modelValue prop
68
+ * */
69
+ modelValue: {
70
+ type: [Number, String],
71
+ default: 0,
72
+ },
61
73
  },
62
- data() {
63
- return {
64
- mdiChevronDown,
65
- };
66
- },
67
- methods: {
68
- onChange(event) {
74
+ emits,
75
+ setup(props, context) {
76
+ const { emit } = context;
77
+
78
+ const {
79
+ classes,
80
+ styles,
81
+ inputAttrs,
82
+ inputListeners,
83
+ } = useAttrs(context, emits);
84
+
85
+ const onChange = (event) => {
69
86
  /**
70
87
  * The value that the select has changed to
71
88
  * @event change
72
89
  * @type {Event}
73
90
  */
74
- this.$emit('change', event.target.value);
75
- },
91
+ emit('change', event.target.value);
92
+ emit('update:modelValue', event.target.value);
93
+ };
94
+ return {
95
+ mdiChevronDown,
96
+ onChange,
97
+ classes,
98
+ styles,
99
+ inputAttrs,
100
+ inputListeners,
101
+ };
76
102
  },
77
103
  };
78
104
  </script>
package/vue/KvSwitch.vue CHANGED
@@ -1,17 +1,21 @@
1
1
  <template>
2
- <div>
2
+ <div
3
+ :class="classes"
4
+ :style="styles"
5
+ >
3
6
  <label
4
- class="tw-inline-flex tw-gap-2 tw-items-center tw-relative"
7
+ class="tw-inline-flex tw-gap-2 tw-items-center tw-relative hover:tw-cursor-pointer"
5
8
  :class="{ 'tw-opacity-low': disabled }"
6
9
  :for="uuid"
7
10
  >
8
11
  <input
9
12
  :id="uuid"
10
- v-bind="$attrs"
13
+ v-bind="inputAttrs"
11
14
  ref="switchRef"
12
15
  class="tw-sr-only tw-peer"
13
16
  type="checkbox"
14
17
  role="switch"
18
+ :checked="modelValue"
15
19
  :disabled="disabled"
16
20
  v-on="inputListeners"
17
21
  @change.prevent="onChange"
@@ -46,7 +50,16 @@
46
50
  </template>
47
51
 
48
52
  <script>
53
+ import {
54
+ ref,
55
+ onMounted,
56
+ } from 'vue-demi';
49
57
  import { nanoid } from 'nanoid';
58
+ import { useAttrs } from '../utils/attrs';
59
+
60
+ const emits = [
61
+ 'update:modelValue',
62
+ ];
50
63
 
51
64
  /**
52
65
  * KvSwitch
@@ -68,14 +81,14 @@ export default {
68
81
  inheritAttrs: false,
69
82
  // v-model will change when checked value changes
70
83
  model: {
71
- prop: 'checked',
72
- event: 'change',
84
+ prop: 'modelValue',
85
+ event: 'update:modelValue',
73
86
  },
74
87
  props: {
75
88
  /**
76
89
  * Whether the switch is on or off
77
90
  * */
78
- checked: {
91
+ modelValue: {
79
92
  type: Boolean,
80
93
  default: false,
81
94
  },
@@ -87,35 +100,44 @@ export default {
87
100
  default: false,
88
101
  },
89
102
  },
90
- data() {
103
+ emits,
104
+ setup(props, context) {
105
+ const { emit } = context;
106
+ const uuid = ref(`kvs-${nanoid(10)}`);
107
+ const switchRef = ref(null);
108
+
109
+ const {
110
+ classes,
111
+ styles,
112
+ inputAttrs,
113
+ inputListeners,
114
+ } = useAttrs(context, emits);
115
+
116
+ const onChange = (event) => {
117
+ emit('update:modelValue', event.target.checked);
118
+ };
119
+
120
+ const focus = () => {
121
+ switchRef.value.focus();
122
+ };
123
+ const blur = () => {
124
+ switchRef.value.blur();
125
+ };
126
+
127
+ onMounted(() => {
128
+ uuid.value = `kvs-${nanoid(10)}`;
129
+ });
130
+
91
131
  return {
92
- uuid: `kvs-${nanoid(10)}`,
132
+ uuid,
133
+ onChange,
134
+ focus,
135
+ blur,
136
+ classes,
137
+ styles,
138
+ inputAttrs,
139
+ inputListeners,
93
140
  };
94
141
  },
95
- computed: {
96
- inputListeners() {
97
- return {
98
- // Pass through any listeners from the parent to the input element, like blur, focus, etc.
99
- // https://vuejs.org/v2/guide/components-custom-events.html#Binding-Native-Events-to-Components
100
- ...this.$listeners,
101
- // ...except for the listener to the 'change' event which is emitted by this component
102
- change: () => {},
103
- };
104
- },
105
- },
106
- mounted() {
107
- this.uuid = `kvs-${nanoid(10)}`;
108
- },
109
- methods: {
110
- onChange(event) {
111
- this.$emit('change', event.target.checked);
112
- },
113
- focus() {
114
- this.$refs.switchRef.focus();
115
- },
116
- blur() {
117
- this.$refs.switchRef.blur();
118
- },
119
- },
120
142
  };
121
143
  </script>
package/vue/KvTab.vue CHANGED
@@ -1,11 +1,11 @@
1
1
  <template>
2
2
  <button
3
- :id="`kv-tab-${this.for}`"
3
+ :id="`kv-tab-${forPanel}`"
4
4
  class="tw-text-h3 tw-mb-1.5 tw-whitespace-nowrap"
5
5
  :class="{ 'hover:tw-text-action-highlight' : !isActive }"
6
6
  role="tab"
7
7
  :aria-selected="isActive"
8
- :aria-controls="`kv-tab-panel-${this.for}`"
8
+ :aria-controls="`kv-tab-panel-${forPanel}`"
9
9
  :tabindex="isActive ? null : -1"
10
10
  @click="handleTabClicked"
11
11
  >
@@ -14,14 +14,21 @@
14
14
  </template>
15
15
 
16
16
  <script>
17
+ import {
18
+ toRefs,
19
+ inject,
20
+ computed,
21
+ onMounted,
22
+ getCurrentInstance,
23
+ } from 'vue-demi';
24
+
17
25
  export default {
18
- inject: ['$KvTabContext'],
19
26
  props: {
20
27
  /**
21
28
  * A unique id which correspondes to an `id` property on the KvTabPanel it controls
22
29
  * e.g., <kv-tab for="foo">... <kv-tab-panel id="foo">
23
30
  * */
24
- for: {
31
+ forPanel: {
25
32
  type: String,
26
33
  required: true,
27
34
  },
@@ -33,23 +40,44 @@ export default {
33
40
  default: false,
34
41
  },
35
42
  },
36
- computed: {
37
- isActive() {
38
- const { navItems, selectedIndex } = this.$KvTabContext;
39
- return navItems[selectedIndex]?.for === this.for;
40
- },
41
- index() {
42
- const { navItems } = this.$KvTabContext;
43
- return navItems?.findIndex((navItem) => navItem.for === this.for);
44
- },
45
- },
46
- mounted() {
47
- this.$KvTabContext.navItems.push(this);
48
- },
49
- methods: {
50
- handleTabClicked() {
51
- this.$KvTabContext.setTab(this.index);
52
- },
43
+ setup(props) {
44
+ const {
45
+ forPanel,
46
+ } = toRefs(props);
47
+
48
+ const kvTabContext = inject('$KvTabContext');
49
+
50
+ const isActive = computed(() => {
51
+ let navItems = [];
52
+ let selectedIndex = 0;
53
+ if (kvTabContext) {
54
+ navItems = kvTabContext.navItems;
55
+ selectedIndex = kvTabContext.selectedIndex;
56
+ }
57
+ return navItems[selectedIndex]?.forPanel === forPanel.value;
58
+ });
59
+
60
+ const index = computed(() => {
61
+ let navItems = [];
62
+ if (kvTabContext) {
63
+ navItems = kvTabContext.navItems;
64
+ }
65
+ return navItems?.findIndex((navItem) => navItem.forPanel === forPanel.value);
66
+ });
67
+
68
+ const handleTabClicked = () => {
69
+ kvTabContext.setTab(index.value);
70
+ };
71
+
72
+ onMounted(() => {
73
+ const instance = getCurrentInstance();
74
+ kvTabContext.navItems.push(instance.proxy);
75
+ });
76
+
77
+ return {
78
+ isActive,
79
+ handleTabClicked,
80
+ };
53
81
  },
54
82
  };
55
83
  </script>
@@ -22,8 +22,13 @@
22
22
  </template>
23
23
 
24
24
  <script>
25
+ import {
26
+ computed,
27
+ toRefs,
28
+ inject,
29
+ } from 'vue-demi';
30
+
25
31
  export default {
26
- inject: ['$KvTabContext'],
27
32
  props: {
28
33
  /**
29
34
  * A unique id which correspondes to a `for` property on the KvTab which controls it
@@ -34,11 +39,26 @@ export default {
34
39
  required: true,
35
40
  },
36
41
  },
37
- computed: {
38
- isActive() {
39
- const { navItems, selectedIndex } = this.$KvTabContext;
40
- return navItems[selectedIndex]?.for === this.id;
41
- },
42
+ setup(props) {
43
+ const {
44
+ id,
45
+ } = toRefs(props);
46
+
47
+ const kvTabContext = inject('$KvTabContext');
48
+
49
+ const isActive = computed(() => {
50
+ let navItems = [];
51
+ let selectedIndex = 0;
52
+ if (kvTabContext) {
53
+ navItems = kvTabContext.navItems;
54
+ selectedIndex = kvTabContext.selectedIndex;
55
+ }
56
+ return navItems[selectedIndex]?.forPanel === id.value;
57
+ });
58
+
59
+ return {
60
+ isActive,
61
+ };
42
62
  },
43
63
  };
44
64
  </script>
package/vue/KvTabs.vue CHANGED
@@ -7,7 +7,7 @@
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
- @keydown="handleKeyDown"
10
+ @keydown="handleKeyDown($event)"
11
11
  >
12
12
  <!-- @slot Tab Navigation -->
13
13
  <slot name="tabNav"></slot>
@@ -54,63 +54,54 @@
54
54
  * </kv-tabs>
55
55
  * ```
56
56
  */
57
+ import {
58
+ ref,
59
+ reactive,
60
+ provide,
61
+ computed,
62
+ onMounted,
63
+ getCurrentInstance,
64
+ onBeforeUnmount,
65
+ } from 'vue-demi';
66
+
57
67
  export default {
58
- provide() {
59
- return {
60
- // Since KvTab and KvTabPanel are tightly coupled to this component we provide
61
- // them with a shared context for setting and reading the state of our tabs
62
- $KvTabContext: this.tabContext,
63
- };
64
- },
65
- data() {
66
- return {
67
- tabContext: {
68
- selectedIndex: 0,
69
- setTab: this.setTab,
70
- navItems: [], // populated by KvTab
71
- },
72
- selectedTabResizeObserver: null,
73
- };
74
- },
75
- computed: {
76
- selectedTabEl() {
77
- const { navItems, selectedIndex } = this.tabContext;
78
- return navItems[selectedIndex]?.$el ?? null;
79
- },
80
- },
81
- mounted() {
82
- // check if any of the KvTab components are declaratively selected
83
- this.tabContext.navItems.forEach((navItem, index) => {
84
- if (navItem.selected) {
85
- this.setTab(index);
86
- }
68
+ setup(props, { emit }) {
69
+ const tabContext = reactive({
70
+ selectedIndex: 0,
71
+ setTab: null,
72
+ navItems: [],
87
73
  });
74
+ const selectedTabResizeObserver = ref(null);
88
75
 
89
- // Tab size can change as @font-face fonts come in or
90
- // the screen breakpoint changes the font size. If this happens
91
- // we need to re-size and position the indicator bar.
92
- this.selectedTabResizeObserver = new ResizeObserver(() => {
93
- this.$forceUpdate();
76
+ const selectedTabEl = computed(() => {
77
+ const { navItems, selectedIndex } = tabContext;
78
+ return navItems[selectedIndex]?.$el ?? null;
94
79
  });
95
- this.selectedTabResizeObserver.observe(this.selectedTabEl);
96
- },
97
- beforeDestroy() {
98
- this.selectedTabResizeObserver.disconnect();
99
- },
100
- methods: {
101
- setTab(index) {
102
- this.tabContext.selectedIndex = index;
103
- this.selectedTabEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
80
+
81
+ const forceUpdate = () => {
82
+ const instance = getCurrentInstance();
83
+ if (instance) {
84
+ instance.proxy.$forceUpdate();
85
+ }
86
+ };
87
+
88
+ const setTab = (index) => {
89
+ tabContext.selectedIndex = index;
90
+ selectedTabEl.value.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
104
91
 
105
92
  /**
106
93
  * Triggers when the selected tab changes
107
94
  *
108
95
  * @property {number} index Index of the newly selected tab
109
96
  */
110
- this.$emit('tab-changed', index);
111
- },
112
- handleKeyDown(event) {
113
- const { navItems, selectedIndex } = this.tabContext;
97
+ emit('tab-changed', index);
98
+ };
99
+
100
+ tabContext.setTab = setTab; // setTab definition in tab context
101
+ provide('$KvTabContext', tabContext);
102
+
103
+ const handleKeyDown = (event) => {
104
+ const { navItems, selectedIndex } = tabContext;
114
105
 
115
106
  const focusActiveTab = () => {
116
107
  const activeTab = navItems
@@ -124,29 +115,55 @@ export default {
124
115
  if (event.key === 'ArrowRight') {
125
116
  event.preventDefault();
126
117
  const nextIndex = (selectedIndex + 1) % count;
127
- this.setTab(nextIndex);
118
+ setTab(nextIndex);
128
119
  focusActiveTab();
129
120
  }
130
121
 
131
122
  if (event.key === 'ArrowLeft') {
132
123
  event.preventDefault();
133
124
  const prevIndex = (selectedIndex - 1 + count) % count;
134
- this.setTab(prevIndex);
125
+ setTab(prevIndex);
135
126
  focusActiveTab();
136
127
  }
137
128
 
138
129
  if (event.key === 'Home') {
139
130
  event.preventDefault();
140
- this.setTab(0);
131
+ setTab(0);
141
132
  focusActiveTab();
142
133
  }
143
134
 
144
135
  if (event.key === 'End') {
145
136
  event.preventDefault();
146
- this.setTab(count - 1);
137
+ setTab(count - 1);
147
138
  focusActiveTab();
148
139
  }
149
- },
140
+ };
141
+
142
+ onMounted(() => {
143
+ // check if any of the KvTab components are declaratively selected
144
+ tabContext.navItems.forEach((navItem, index) => {
145
+ if (navItem.selected) {
146
+ setTab(index);
147
+ }
148
+ });
149
+
150
+ // Tab size can change as @font-face fonts come in or
151
+ // the screen breakpoint changes the font size. If this happens
152
+ // we need to re-size and position the indicator bar.
153
+ selectedTabResizeObserver.value = new ResizeObserver(() => {
154
+ forceUpdate();
155
+ });
156
+ selectedTabResizeObserver.value.observe(selectedTabEl.value);
157
+ });
158
+
159
+ onBeforeUnmount(() => {
160
+ selectedTabResizeObserver.value.disconnect();
161
+ });
162
+
163
+ return {
164
+ handleKeyDown,
165
+ selectedTabEl,
166
+ };
150
167
  },
151
168
  };
152
169
  </script>