srcdev-nuxt-components 0.0.27 → 0.0.29
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.
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
<div class="tabs">
|
|
3
|
+
<ul role="tablist" aria-labelledby="channel-name" ref="tabsNavRef" @mouseleave="resetHoverToActivePosition()" class="tabs-list" :class="[elementClasses]">
|
|
4
|
+
<li v-for="(index, key) in navItems" :key="key">
|
|
5
|
+
<button
|
|
6
|
+
@click.prevent="navItemClicked($event)"
|
|
7
|
+
@mouseover="navItemHovered($event)"
|
|
8
|
+
:id="`tab-${key}-trigger`"
|
|
9
|
+
:data-tab-index="index"
|
|
10
|
+
data-nav-item
|
|
11
|
+
role="tab"
|
|
12
|
+
aria-selected="false"
|
|
13
|
+
class="tabs-list-item"
|
|
14
|
+
>
|
|
15
|
+
<slot :name="`tab-${key}-trigger`"></slot>
|
|
16
|
+
</button>
|
|
17
|
+
</li>
|
|
18
|
+
</ul>
|
|
19
|
+
<div class="tab-content-wrapper">
|
|
20
|
+
<div v-for="(item, key) in navItems" :key="key" class="tab-content" :aria-labelledby="`tab-${key}-trigger`" :id="`tab-${key}-content`" role="region" aria-hidden="true" ref="tabsContentRefs">
|
|
21
|
+
<div>
|
|
22
|
+
<slot :name="`tab-${key}-content`"></slot>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
9
27
|
</template>
|
|
10
28
|
|
|
11
29
|
<script setup lang="ts">
|
|
12
|
-
|
|
13
|
-
action?: string;
|
|
14
|
-
name: string;
|
|
15
|
-
path?: string;
|
|
16
|
-
}
|
|
30
|
+
import type { ITabNav } from '@/types/types.tabs';
|
|
17
31
|
|
|
18
32
|
const props = defineProps({
|
|
19
33
|
tag: {
|
|
@@ -21,7 +35,7 @@ const props = defineProps({
|
|
|
21
35
|
default: 'button',
|
|
22
36
|
},
|
|
23
37
|
navItems: {
|
|
24
|
-
type: Array as PropType<
|
|
38
|
+
type: Array as PropType<ITabNav[]>,
|
|
25
39
|
required: true,
|
|
26
40
|
},
|
|
27
41
|
trackHover: {
|
|
@@ -44,9 +58,10 @@ const props = defineProps({
|
|
|
44
58
|
|
|
45
59
|
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
46
60
|
|
|
47
|
-
const
|
|
61
|
+
const tabsNavRef = ref<HTMLElement | null>(null);
|
|
62
|
+
const tabsContentRefs = ref<HTMLElement[] | null>(null);
|
|
48
63
|
|
|
49
|
-
const { initNavDecorators, navItemClicked, navItemHovered, resetHoverToActivePosition } =
|
|
64
|
+
const { initNavDecorators, navItemClicked, navItemHovered, resetHoverToActivePosition } = useTabs(tabsNavRef, tabsContentRefs);
|
|
50
65
|
|
|
51
66
|
onMounted(() => {
|
|
52
67
|
initNavDecorators();
|
|
@@ -55,13 +70,16 @@ onMounted(() => {
|
|
|
55
70
|
|
|
56
71
|
<style lang="css">
|
|
57
72
|
.tabs-list {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
--
|
|
62
|
-
--
|
|
63
|
-
--
|
|
64
|
-
--
|
|
73
|
+
/*
|
|
74
|
+
* CSS var within /assets/styles/components/tabs.css
|
|
75
|
+
*/
|
|
76
|
+
--_tabs-default-text: var(--tabs-default-text, light-dark(var(--gray-12), var(--gray-0)));
|
|
77
|
+
--_tabs-active-bg: var(--tabs-active-bg, light-dark(var(--gray-12), var(--gray-0)));
|
|
78
|
+
--_tabs-active-text: var(--tabs-active-text, light-dark(var(--gray-0), var(--gray-12)));
|
|
79
|
+
--_tabs-active-indicator: var(--tabs-active-indicator, light-dark(var(--gray-12), var(--gray-0)));
|
|
80
|
+
--_tabs-hovered-bg: var(--tabs-hovered-bg, light-dark(var(--gray-7), var(--gray-3)));
|
|
81
|
+
--_tabs-hovered-text: var(--tabs-hovered-text, light-dark(var(--gray-0), var(--gray-12)));
|
|
82
|
+
--_tabs-border-bottom: var(--tabs-border-bottom, light-dark(var(--gray-12), var(--gray-0)));
|
|
65
83
|
|
|
66
84
|
position: relative;
|
|
67
85
|
display: flex;
|
|
@@ -130,28 +148,28 @@ onMounted(() => {
|
|
|
130
148
|
/*
|
|
131
149
|
* User configurable variables
|
|
132
150
|
*/
|
|
133
|
-
border-bottom: 1px solid var(--
|
|
151
|
+
border-bottom: 1px solid var(--_tabs-border-bottom);
|
|
134
152
|
margin-block: 3rem;
|
|
135
153
|
|
|
136
154
|
.nav__hovered {
|
|
137
|
-
background: var(--
|
|
138
|
-
color: var(--
|
|
155
|
+
background: var(--_tabs-hovered-bg);
|
|
156
|
+
color: var(--_tabs-hovered-text);
|
|
139
157
|
}
|
|
140
158
|
|
|
141
159
|
.nav__active {
|
|
142
|
-
background: var(--
|
|
143
|
-
color: var(--
|
|
160
|
+
background: var(--_tabs-active-bg);
|
|
161
|
+
color: var(--_tabs-active-text);
|
|
144
162
|
}
|
|
145
163
|
|
|
146
164
|
.nav__active-indicator {
|
|
147
|
-
background: var(--
|
|
165
|
+
background: var(--_tabs-active-indicator);
|
|
148
166
|
height: 4px;
|
|
149
167
|
}
|
|
150
168
|
|
|
151
169
|
.tabs-list-item {
|
|
152
170
|
background: transparent;
|
|
153
171
|
border: 0;
|
|
154
|
-
color: var(--
|
|
172
|
+
color: var(--_tabs-default-text);
|
|
155
173
|
cursor: pointer;
|
|
156
174
|
font: inherit;
|
|
157
175
|
text-transform: uppercase;
|
|
@@ -160,12 +178,22 @@ onMounted(() => {
|
|
|
160
178
|
padding: 1em 2em;
|
|
161
179
|
|
|
162
180
|
&:hover {
|
|
163
|
-
color: var(--
|
|
181
|
+
color: var(--_tabs-hovered-text);
|
|
164
182
|
}
|
|
165
183
|
|
|
166
184
|
&[aria-selected='true'] {
|
|
167
|
-
color: var(--
|
|
185
|
+
color: var(--_tabs-active-text);
|
|
168
186
|
}
|
|
169
187
|
}
|
|
170
188
|
}
|
|
189
|
+
|
|
190
|
+
.tab-content-wrapper {
|
|
191
|
+
display: grid;
|
|
192
|
+
grid-template-areas: 'element-stack';
|
|
193
|
+
|
|
194
|
+
.tab-content {
|
|
195
|
+
grid-area: element-stack;
|
|
196
|
+
display: none;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
171
199
|
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useResizeObserver } from '@vueuse/core';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const useTabs = (tabsNavRef: Ref<HTMLElement | null>, tabsContentRefs: Ref<HTMLElement[] | null>, duration: number = 200) => {
|
|
4
4
|
const navItems = ref<HTMLElement[] | null>(null);
|
|
5
5
|
const previousActiveTab = useState<HTMLElement | null>('previousActiveTab', () => null);
|
|
6
6
|
const currentActiveTab = ref<HTMLElement>();
|
|
@@ -10,7 +10,7 @@ const useNavDecoration = (navContainerRef: Ref<HTMLElement | null>, duration: nu
|
|
|
10
10
|
const tagName = ref<string>();
|
|
11
11
|
|
|
12
12
|
const initNavDecorators = () => {
|
|
13
|
-
navItems.value =
|
|
13
|
+
navItems.value = tabsNavRef.value ? (Array.from(tabsNavRef.value.querySelectorAll('[data-nav-item')) as HTMLElement[]) : [];
|
|
14
14
|
tagName.value = navItems.value[0].tagName.toLowerCase();
|
|
15
15
|
|
|
16
16
|
const activeIndex = ref(0);
|
|
@@ -34,15 +34,16 @@ const useNavDecoration = (navContainerRef: Ref<HTMLElement | null>, duration: nu
|
|
|
34
34
|
previousHoveredTab.value = navItems.value[activeIndex.value];
|
|
35
35
|
|
|
36
36
|
addNavDecorators();
|
|
37
|
+
setActiveTabContent();
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
const addNavDecorators = () => {
|
|
40
41
|
const elementClasses = ['nav__active-indicator', 'nav__active', 'nav__hovered'];
|
|
41
|
-
if (
|
|
42
|
+
if (tabsNavRef.value) {
|
|
42
43
|
for (let i = 0; i < 3; i++) {
|
|
43
44
|
const div = document.createElement('div');
|
|
44
45
|
div.classList.add(elementClasses[i]);
|
|
45
|
-
|
|
46
|
+
tabsNavRef.value.appendChild(div);
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
};
|
|
@@ -75,26 +76,27 @@ const useNavDecoration = (navContainerRef: Ref<HTMLElement | null>, duration: nu
|
|
|
75
76
|
});
|
|
76
77
|
|
|
77
78
|
moveActiveIndicator();
|
|
79
|
+
setActiveTabContent();
|
|
78
80
|
};
|
|
79
81
|
|
|
80
82
|
const setFinalHoveredPositions = (resized: boolean = false) => {
|
|
81
83
|
const setDuration = resized ? 0 : duration;
|
|
82
|
-
const newTabWidth = currentHoveredTab.value &&
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
const newTabWidth = currentHoveredTab.value && tabsNavRef.value ? currentHoveredTab.value.offsetWidth / tabsNavRef.value.offsetWidth : 0;
|
|
85
|
+
tabsNavRef.value?.style.setProperty('--_transition-duration', setDuration + 'ms');
|
|
86
|
+
tabsNavRef.value?.style.setProperty('--_left-hovered', currentHoveredTab.value?.offsetLeft + 'px');
|
|
87
|
+
tabsNavRef.value?.style.setProperty('--_width-hovered', newTabWidth?.toString());
|
|
86
88
|
};
|
|
87
89
|
|
|
88
90
|
const setFinalActivePositions = (resized: boolean = false) => {
|
|
89
91
|
const setDuration = resized ? 0 : duration;
|
|
90
|
-
const newTabWidth = currentActiveTab.value &&
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
const newTabWidth = currentActiveTab.value && tabsNavRef.value ? currentActiveTab.value.offsetWidth / tabsNavRef.value.offsetWidth : 0;
|
|
93
|
+
tabsNavRef.value?.style.setProperty('--_transition-duration', setDuration + 'ms');
|
|
94
|
+
tabsNavRef.value?.style.setProperty('--_left-active', currentActiveTab.value?.offsetLeft + 'px');
|
|
95
|
+
tabsNavRef.value?.style.setProperty('--_width-active', newTabWidth?.toString());
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
const moveActiveIndicator = () => {
|
|
97
|
-
|
|
99
|
+
tabsNavRef.value?.style.setProperty('--_transition-duration', duration + 'ms');
|
|
98
100
|
|
|
99
101
|
const newTabPosition = previousActiveTab.value && currentActiveTab.value ? previousActiveTab.value.compareDocumentPosition(currentActiveTab.value) : 0;
|
|
100
102
|
let transitionWidth;
|
|
@@ -103,10 +105,10 @@ const useNavDecoration = (navContainerRef: Ref<HTMLElement | null>, duration: nu
|
|
|
103
105
|
transitionWidth = currentActiveTab.value && previousActiveTab.value ? currentActiveTab.value.offsetLeft + currentActiveTab.value.offsetWidth - previousActiveTab.value.offsetLeft : 0;
|
|
104
106
|
} else {
|
|
105
107
|
transitionWidth = previousActiveTab.value && currentActiveTab.value ? previousActiveTab.value.offsetLeft + previousActiveTab.value.offsetWidth - currentActiveTab.value.offsetLeft : 0;
|
|
106
|
-
|
|
108
|
+
tabsNavRef.value?.style.setProperty('--_left-active', currentActiveTab.value ? currentActiveTab.value.offsetLeft + 'px' : '0');
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
|
|
111
|
+
tabsNavRef.value?.style.setProperty('--_width-active', String(transitionWidth / tabsNavRef.value.offsetWidth));
|
|
110
112
|
|
|
111
113
|
setTimeout(() => {
|
|
112
114
|
setFinalActivePositions();
|
|
@@ -114,7 +116,7 @@ const useNavDecoration = (navContainerRef: Ref<HTMLElement | null>, duration: nu
|
|
|
114
116
|
};
|
|
115
117
|
|
|
116
118
|
const moveHoveredIndicator = () => {
|
|
117
|
-
|
|
119
|
+
tabsNavRef.value?.style.setProperty('--_transition-duration', duration + 'ms');
|
|
118
120
|
|
|
119
121
|
const newTabPosition = previousHoveredTab.value && currentHoveredTab.value ? previousHoveredTab.value.compareDocumentPosition(currentHoveredTab.value) : 0;
|
|
120
122
|
let transitionWidth;
|
|
@@ -123,28 +125,38 @@ const useNavDecoration = (navContainerRef: Ref<HTMLElement | null>, duration: nu
|
|
|
123
125
|
transitionWidth = currentHoveredTab.value && previousHoveredTab.value ? currentHoveredTab.value.offsetLeft + currentHoveredTab.value.offsetWidth - previousHoveredTab.value.offsetLeft : 0;
|
|
124
126
|
} else {
|
|
125
127
|
transitionWidth = previousHoveredTab.value && currentHoveredTab.value ? previousHoveredTab.value.offsetLeft + previousHoveredTab.value.offsetWidth - currentHoveredTab.value.offsetLeft : 0;
|
|
126
|
-
|
|
128
|
+
tabsNavRef.value?.style.setProperty('--_left-hovered', currentHoveredTab.value ? currentHoveredTab.value.offsetLeft + 'px' : '0');
|
|
127
129
|
}
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
tabsNavRef.value?.style.setProperty('--_width-hovered', String(transitionWidth / tabsNavRef.value.offsetWidth));
|
|
130
132
|
|
|
131
133
|
setTimeout(() => {
|
|
132
134
|
setFinalHoveredPositions();
|
|
133
135
|
}, Math.floor(duration + 20));
|
|
134
136
|
};
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
const setActiveTabContent = () => {
|
|
139
|
+
const activeIndex = navItems.value?.findIndex((el) => el === currentActiveTab.value);
|
|
140
|
+
tabsContentRefs.value?.forEach((tabContent: HTMLElement, index: number) => {
|
|
141
|
+
tabContent.style.display = activeIndex === index ? 'block' : 'none';
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const animationRunning = (running: boolean) => {
|
|
146
|
+
console.log('animationRunning', running);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
useResizeObserver(tabsNavRef, () => {
|
|
137
150
|
setFinalActivePositions(true);
|
|
138
151
|
setFinalHoveredPositions(true);
|
|
139
152
|
});
|
|
140
153
|
|
|
141
154
|
return {
|
|
142
155
|
initNavDecorators,
|
|
143
|
-
navContainerRef,
|
|
144
156
|
navItemClicked,
|
|
145
157
|
navItemHovered,
|
|
146
158
|
resetHoverToActivePosition,
|
|
147
159
|
};
|
|
148
160
|
};
|
|
149
161
|
|
|
150
|
-
export default
|
|
162
|
+
export default useTabs;
|
package/package.json
CHANGED