srcdev-nuxt-components 4.0.1 → 4.0.3
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,44 +1,53 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<section class="carousel-basic" :class="[elementClasses]" ref="carouselWrapperRef">
|
|
3
|
-
|
|
4
|
-
<div
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<section class="carousel-basic" :class="[elementClasses]" ref="carouselWrapperRef" role="region" aria-label="Image carousel">
|
|
3
|
+
<!-- Screen reader announcement for current item -->
|
|
4
|
+
<div aria-live="polite" aria-atomic="true" class="sr-only">Item {{ currentIndex + 1 }} of {{ itemCount }}</div>
|
|
5
|
+
|
|
6
|
+
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-20']">
|
|
7
|
+
<div tabindex="0" class="item-container" :class="{ 'allow-overflow': allowCarouselOverflow }" ref="carouselContainerRef" role="group" aria-label="Carousel items">
|
|
8
|
+
<div v-for="(item, index) in carouselDataIds" :key="index" class="item" ref="carouselItems" :aria-current="currentIndex === index ? 'true' : 'false'">
|
|
9
|
+
<slot :name="item"></slot>
|
|
10
|
+
</div>
|
|
7
11
|
</div>
|
|
8
|
-
</
|
|
12
|
+
</LayoutRow>
|
|
9
13
|
|
|
10
|
-
<div class="
|
|
11
|
-
<div
|
|
12
|
-
<div
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<div class="controls-container">
|
|
17
|
-
<div class="markers-container">
|
|
18
|
-
<ul class="markers-list">
|
|
19
|
-
<li v-for="index in itemCount" :key="index" class="markers-item">
|
|
20
|
-
<button @click.prevent="jumpToFrame(index)" class="btn-marker"
|
|
21
|
-
:class="[{ active: currentIndex === index - 1}]"><span class="sr-only">Jump to item{{
|
|
22
|
-
Math.floor(index + 1) }}</span></button>
|
|
23
|
-
</li>
|
|
24
|
-
</ul>
|
|
14
|
+
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-20']">
|
|
15
|
+
<div class="timeline-container">
|
|
16
|
+
<div v-for="index in itemCount" :key="index" class="timeline-item">
|
|
17
|
+
<div class="count">Step {{ index }}</div>
|
|
18
|
+
</div>
|
|
25
19
|
</div>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
</LayoutRow>
|
|
21
|
+
|
|
22
|
+
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-20']">
|
|
23
|
+
<div tabindex="0" class="controls-container" ref="controlsContainerRef">
|
|
24
|
+
<div class="markers-container">
|
|
25
|
+
<ul class="markers-list">
|
|
26
|
+
<li v-for="index in itemCount" :key="index" class="markers-item">
|
|
27
|
+
<button @click.prevent="jumpToFrame(index - 1)" class="btn-marker" :class="[{ active: currentIndex === index - 1 }]" :aria-label="`Jump to item ${Math.floor(index + 1)}`"></button>
|
|
28
|
+
</li>
|
|
29
|
+
</ul>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="buttons-container">
|
|
32
|
+
<button type="button" @click.prevent="actionPrevious()" class="btn-action" aria-label="Go to previous item">
|
|
33
|
+
<Icon name="ic:outline-keyboard-arrow-left" class="arrows-icon" />
|
|
34
|
+
</button>
|
|
35
|
+
<button type="button" @click.prevent="actionNext()" class="btn-action" aria-label="Go to next item">
|
|
36
|
+
<Icon name="ic:outline-keyboard-arrow-right" class="arrows-icon" />
|
|
37
|
+
</button>
|
|
38
|
+
</div>
|
|
29
39
|
</div>
|
|
30
|
-
</
|
|
40
|
+
</LayoutRow>
|
|
31
41
|
</section>
|
|
32
42
|
</template>
|
|
33
43
|
|
|
34
44
|
<script setup lang="ts">
|
|
35
|
-
import
|
|
36
|
-
import { useEventListener, useResizeObserver, useSwipe } from "@vueuse/core";
|
|
45
|
+
import { useEventListener, useResizeObserver, useSwipe } from '@vueuse/core';
|
|
37
46
|
|
|
38
47
|
const props = defineProps({
|
|
39
48
|
carouselDataIds: {
|
|
40
49
|
type: Array as PropType<string[]>,
|
|
41
|
-
default: () => []
|
|
50
|
+
default: () => [],
|
|
42
51
|
},
|
|
43
52
|
styleClassPassthrough: {
|
|
44
53
|
type: Array as PropType<string[]>,
|
|
@@ -46,8 +55,12 @@ const props = defineProps({
|
|
|
46
55
|
},
|
|
47
56
|
transitionSpeed: {
|
|
48
57
|
type: Number,
|
|
49
|
-
default: 200
|
|
50
|
-
}
|
|
58
|
+
default: 200,
|
|
59
|
+
},
|
|
60
|
+
allowCarouselOverflow: {
|
|
61
|
+
type: Boolean,
|
|
62
|
+
default: false,
|
|
63
|
+
},
|
|
51
64
|
});
|
|
52
65
|
|
|
53
66
|
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
@@ -55,6 +68,7 @@ const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
|
55
68
|
const carouselWrapperRef = ref<HTMLDivElement | null>(null);
|
|
56
69
|
const carouselContainerRef = ref<HTMLDivElement | null>(null);
|
|
57
70
|
const carouselItemsRef = useTemplateRef<HTMLDivElement[]>('carouselItems');
|
|
71
|
+
const controlsContainerRef = ref<HTMLDivElement | null>(null);
|
|
58
72
|
const carouselInitComplete = ref(false);
|
|
59
73
|
|
|
60
74
|
const currentIndex = ref(0);
|
|
@@ -63,7 +77,7 @@ const offset = ref(0);
|
|
|
63
77
|
const transitionSpeedStr = props.transitionSpeed + 'ms';
|
|
64
78
|
const itemTransform = computed(() => {
|
|
65
79
|
return `translateX(calc(${offset.value} * (${itemWidth.value} + var(--_item-gap))))`;
|
|
66
|
-
})
|
|
80
|
+
});
|
|
67
81
|
|
|
68
82
|
const itemWidth = ref('0px');
|
|
69
83
|
|
|
@@ -74,7 +88,7 @@ const actionPrevious = () => {
|
|
|
74
88
|
|
|
75
89
|
offset.value = Math.min(offset.value + 1);
|
|
76
90
|
doAction();
|
|
77
|
-
}
|
|
91
|
+
};
|
|
78
92
|
|
|
79
93
|
const actionNext = () => {
|
|
80
94
|
if (offset.value <= -1 * (itemCount.value - 1)) {
|
|
@@ -83,19 +97,18 @@ const actionNext = () => {
|
|
|
83
97
|
|
|
84
98
|
offset.value = Math.min(offset.value - 1);
|
|
85
99
|
doAction();
|
|
86
|
-
}
|
|
100
|
+
};
|
|
87
101
|
|
|
88
102
|
const doAction = () => {
|
|
89
103
|
currentIndex.value = Math.abs(offset.value);
|
|
90
|
-
}
|
|
104
|
+
};
|
|
91
105
|
|
|
92
106
|
const jumpToFrame = (index: number) => {
|
|
93
|
-
|
|
94
107
|
if (index >= 0 && index < itemCount.value) {
|
|
95
108
|
offset.value = -index;
|
|
96
109
|
doAction();
|
|
97
110
|
}
|
|
98
|
-
}
|
|
111
|
+
};
|
|
99
112
|
|
|
100
113
|
const initialSetup = () => {
|
|
101
114
|
if (carouselItemsRef?.value && carouselItemsRef.value.length > 0 && carouselItemsRef.value[0]) {
|
|
@@ -103,33 +116,34 @@ const initialSetup = () => {
|
|
|
103
116
|
}
|
|
104
117
|
|
|
105
118
|
carouselInitComplete.value = true;
|
|
106
|
-
}
|
|
119
|
+
};
|
|
107
120
|
|
|
108
|
-
const { direction } = useSwipe(
|
|
109
|
-
|
|
110
|
-
{
|
|
111
|
-
|
|
112
|
-
onSwipeEnd() {
|
|
113
|
-
if (direction.value === 'left') {
|
|
114
|
-
actionNext();
|
|
115
|
-
} else if (direction.value === 'right') {
|
|
116
|
-
actionPrevious();
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
useEventListener(
|
|
123
|
-
carouselContainerRef,
|
|
124
|
-
'keydown',
|
|
125
|
-
(event: KeyboardEvent) => {
|
|
126
|
-
if (event.key === 'ArrowLeft') {
|
|
127
|
-
actionPrevious();
|
|
128
|
-
} else if (event.key === 'ArrowRight') {
|
|
121
|
+
const { direction } = useSwipe(carouselContainerRef, {
|
|
122
|
+
passive: false,
|
|
123
|
+
onSwipeEnd() {
|
|
124
|
+
if (direction.value === 'left') {
|
|
129
125
|
actionNext();
|
|
126
|
+
} else if (direction.value === 'right') {
|
|
127
|
+
actionPrevious();
|
|
130
128
|
}
|
|
131
129
|
},
|
|
132
|
-
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
useEventListener(carouselContainerRef, 'keydown', (event: KeyboardEvent) => {
|
|
133
|
+
if (event.key === 'ArrowLeft') {
|
|
134
|
+
actionPrevious();
|
|
135
|
+
} else if (event.key === 'ArrowRight') {
|
|
136
|
+
actionNext();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
useEventListener(controlsContainerRef, 'keydown', (event: KeyboardEvent) => {
|
|
141
|
+
if (event.key === 'ArrowLeft') {
|
|
142
|
+
actionPrevious();
|
|
143
|
+
} else if (event.key === 'ArrowRight') {
|
|
144
|
+
actionNext();
|
|
145
|
+
}
|
|
146
|
+
});
|
|
133
147
|
|
|
134
148
|
useResizeObserver(carouselWrapperRef, async () => {
|
|
135
149
|
initialSetup();
|
|
@@ -138,11 +152,9 @@ useResizeObserver(carouselWrapperRef, async () => {
|
|
|
138
152
|
onMounted(() => {
|
|
139
153
|
initialSetup();
|
|
140
154
|
});
|
|
141
|
-
|
|
142
155
|
</script>
|
|
143
156
|
|
|
144
157
|
<style lang="css">
|
|
145
|
-
|
|
146
158
|
.carousel-basic {
|
|
147
159
|
--_item-gap: 10px;
|
|
148
160
|
|
|
@@ -150,6 +162,18 @@ onMounted(() => {
|
|
|
150
162
|
grid-template-columns: 1fr;
|
|
151
163
|
gap: 10px;
|
|
152
164
|
|
|
165
|
+
.sr-only {
|
|
166
|
+
position: absolute;
|
|
167
|
+
width: 1px;
|
|
168
|
+
height: 1px;
|
|
169
|
+
padding: 0;
|
|
170
|
+
margin: -1px;
|
|
171
|
+
overflow: hidden;
|
|
172
|
+
clip: rect(0, 0, 0, 0);
|
|
173
|
+
white-space: nowrap;
|
|
174
|
+
border: 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
153
177
|
.timeline-container {
|
|
154
178
|
display: flex;
|
|
155
179
|
gap: var(--_item-gap);
|
|
@@ -184,6 +208,10 @@ onMounted(() => {
|
|
|
184
208
|
overflow-x: hidden;
|
|
185
209
|
position: relative;
|
|
186
210
|
|
|
211
|
+
&.allow-overflow {
|
|
212
|
+
overflow-x: initial;
|
|
213
|
+
}
|
|
214
|
+
|
|
187
215
|
.item {
|
|
188
216
|
display: flex;
|
|
189
217
|
flex: 0 0 100%;
|
|
@@ -199,7 +227,6 @@ onMounted(() => {
|
|
|
199
227
|
justify-content: flex-end;
|
|
200
228
|
|
|
201
229
|
.markers-container {
|
|
202
|
-
|
|
203
230
|
.markers-list {
|
|
204
231
|
display: flex;
|
|
205
232
|
flex-direction: row;
|
|
@@ -209,7 +236,6 @@ onMounted(() => {
|
|
|
209
236
|
padding: unset;
|
|
210
237
|
|
|
211
238
|
.markers-item {
|
|
212
|
-
|
|
213
239
|
.btn-marker {
|
|
214
240
|
border: none;
|
|
215
241
|
outline: none;
|
|
@@ -232,8 +258,17 @@ onMounted(() => {
|
|
|
232
258
|
gap: 20px;
|
|
233
259
|
|
|
234
260
|
.btn-action {
|
|
261
|
+
display: flex;
|
|
262
|
+
align-items: center;
|
|
263
|
+
justify-content: center;
|
|
264
|
+
|
|
235
265
|
cursor: pointer;
|
|
236
266
|
height: fit-content;
|
|
267
|
+
|
|
268
|
+
.arrows-icon {
|
|
269
|
+
width: 24px;
|
|
270
|
+
height: 24px;
|
|
271
|
+
}
|
|
237
272
|
}
|
|
238
273
|
}
|
|
239
274
|
}
|
package/nuxt.config.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export default defineNuxtConfig({
|
|
3
3
|
devtools: { enabled: true },
|
|
4
4
|
css: ['modern-normalize', './assets/styles/main.css'],
|
|
5
|
-
modules: ['@nuxt/icon', '@nuxt/image'
|
|
5
|
+
modules: ['@nuxt/icon', '@nuxt/image'],
|
|
6
6
|
app: {
|
|
7
7
|
head: {
|
|
8
8
|
htmlAttrs: {
|
|
@@ -34,7 +34,4 @@ export default defineNuxtConfig({
|
|
|
34
34
|
typescript: {
|
|
35
35
|
includeWorkspace: true,
|
|
36
36
|
},
|
|
37
|
-
eslint: {
|
|
38
|
-
// Add any custom options here
|
|
39
|
-
},
|
|
40
37
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "srcdev-nuxt-components",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.0.
|
|
4
|
+
"version": "4.0.3",
|
|
5
5
|
"main": "nuxt.config.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"clean": "rm -rf .nuxt && rm -rf .output && rm -rf .playground/.nuxt && rm -rf .playground/.output",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@iconify-json/akar-icons": "1.2.2",
|
|
29
29
|
"@iconify-json/bitcoin-icons": "1.2.2",
|
|
30
|
-
"@nuxt/eslint": "1.5.2",
|
|
31
30
|
"@nuxt/eslint-config": "1.5.2",
|
|
32
31
|
"@nuxt/icon": "1.15.0",
|
|
33
32
|
"@nuxt/image": "1.10.0",
|
|
34
33
|
"@oddbird/css-anchor-positioning": "0.6.1",
|
|
35
34
|
"@vueuse/core": "13.5.0",
|
|
35
|
+
"eslint": "9.31.0",
|
|
36
36
|
"happy-dom": "16.8.1",
|
|
37
37
|
"nuxt": "3.17.6",
|
|
38
38
|
"release-it": "18.1.2",
|