srcdev-nuxt-components 2.3.1 → 2.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.
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <component :is="tag" class="display-banner" :class="[elementClasses]">
3
+ <div v-if="$slots.canvas" class="canvas">
4
+ <slot name="canvas"></slot>
5
+ </div>
6
+ <div v-if="$slots.content" class="content">
7
+ <slot name="content"></slot>
8
+ </div>
9
+ </component>
10
+ </template>
11
+
12
+ <script lang="ts">
13
+ const TAGS_ALLOWED = <string[]>['div', 'p', 'span', 'section', 'article', 'aside', 'header', 'footer', 'main', 'nav', 'ul', 'ol'];
14
+ </script>
15
+
16
+ <script setup lang="ts">
17
+
18
+ const props = defineProps({
19
+ tag: {
20
+ type: String,
21
+ default: 'div',
22
+ validator(value: string) {
23
+ return TAGS_ALLOWED.includes(value);
24
+ },
25
+ },
26
+ styleClassPassthrough: {
27
+ type: Array as PropType<string[]>,
28
+ default: () => [],
29
+ },
30
+ });
31
+
32
+ const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
33
+
34
+ watch(
35
+ () => props.styleClassPassthrough,
36
+ () => {
37
+ resetElementClasses(props.styleClassPassthrough);
38
+ }
39
+ );
40
+ </script>
41
+
42
+ <style lang="css">
43
+ .display-banner {
44
+ display: grid;
45
+ grid-template-areas: 'banner';
46
+ container-type: inline-size;
47
+
48
+ .canvas {
49
+ grid-area: banner;
50
+
51
+ .image {
52
+ object-fit: cover;
53
+ width: 100%;
54
+ height: 100%;
55
+ }
56
+ }
57
+
58
+ .content {
59
+ grid-area: banner;
60
+ }
61
+ }
62
+ </style>
@@ -1,14 +1,15 @@
1
1
  <template>
2
2
  <div class="slider-gallery" :class="[elementClasses]" ref="sliderGalleryWrapper">
3
- <div class="loading-state" :class="[{ loaded: !isLoading }]">
3
+ <div class="loading-state" :class="[{ galleryLoaded: !galleryLoaded }]">
4
4
  <div class="loading-spinner"></div>
5
5
  <p>Loading gallery...</p>
6
6
  </div>
7
7
 
8
- <div v-if="showGallery" class="gallery-content" :class="[{ loaded: isLoading }]">
8
+ <div v-if="showGallery" class="gallery-content" :class="[{ galleryLoaded: !galleryLoaded }]">
9
9
  <div class="list" ref="sliderGalleryImagesList">
10
10
  <div v-for="(item, index) in galleryData" :key="index" class="item">
11
- <NuxtImg :src="item.src" :alt="item.alt" @load="handleImageLoad(index)" @error="handleImageError(index)" loading="lazy" />
11
+ <NuxtImg :src="item.src" :alt="item.alt" @load="handleImageLoad(index)" @error="handleImageError(index)"
12
+ loading="lazy" />
12
13
  <div class="content">
13
14
  <div class="author">{{ item.stylist }}</div>
14
15
  <div class="title">{{ item.title }}</div>
@@ -27,15 +28,20 @@
27
28
  <img :src="item.src" :alt="item.alt" loading="lazy" />
28
29
  <div class="content">
29
30
  <div class="title" v-show="item.thumbnail?.title !== ''">{{ item.thumbnail?.title }}</div>
30
- <div class="description" v-show="item.thumbnail?.description !== ''">{{ item.thumbnail?.description }}</div>
31
+ <div class="description" v-show="item.thumbnail?.description !== ''">{{ item.thumbnail?.description }}
32
+ </div>
31
33
  </div>
32
34
  </div>
33
35
  </div>
34
36
  </div>
35
37
 
36
38
  <div class="arrows">
37
- <button id="prev" ref="prevDom" @click.prevent="doPrevious()"><</button>
38
- <button id="next" ref="nextDom" @click.prevent="doNext()">></button>
39
+ <button id="prev" ref="prevDom" @click.prevent="doPrevious()">
40
+ <Icon name="ic:outline-keyboard-arrow-left" class="arrows-icon" />
41
+ </button>
42
+ <button id="next" ref="nextDom" @click.prevent="doNext()">
43
+ <Icon name="ic:outline-keyboard-arrow-right" class="arrows-icon" />
44
+ </button>
39
45
  </div>
40
46
 
41
47
  <div class="time"></div>
@@ -83,7 +89,8 @@ const sliderGalleryWrapper = useTemplateRef('sliderGalleryWrapper');
83
89
  const sliderGalleryImagesList = useTemplateRef('sliderGalleryImagesList');
84
90
  const sliderGalleryThumbnailsList = useTemplateRef('sliderGalleryThumbnailsList');
85
91
 
86
- const isLoading = ref(true);
92
+ const transitionRunning = ref(false);
93
+ const galleryLoaded = ref(true);
87
94
  const showGallery = ref(false);
88
95
  const loadedImages = ref<Set<number>>(new Set());
89
96
  const preloadedImages = ref<Array<HTMLImageElement>>([]);
@@ -93,7 +100,7 @@ onMounted(async () => {
93
100
 
94
101
  // If no images or galleryData is empty, stop loading
95
102
  if (!galleryData.value || galleryData.value.length === 0) {
96
- isLoading.value = false;
103
+ galleryLoaded.value = false;
97
104
  return;
98
105
  }
99
106
 
@@ -127,27 +134,28 @@ onMounted(async () => {
127
134
  await Promise.race(imageLoadPromises);
128
135
 
129
136
  setTimeout(() => {
130
- isLoading.value = false;
137
+ galleryLoaded.value = false;
131
138
  }, 500);
139
+
140
+ showGallery.value = true;
141
+ window.addEventListener('keydown', handleKeyDown);
132
142
  });
133
143
 
134
144
  const handleImageLoad = (index: number) => {
135
- console.log('Image loaded:', index);
136
145
  loadedImages.value.add(index);
137
146
  };
138
147
 
139
148
  const handleImageError = (index: number) => {
140
- console.error(`Failed to load image at index ${index}`);
141
149
  loadedImages.value.add(index);
142
150
  };
143
151
 
144
152
  const doNext = () => {
145
- if (isLoading.value) return;
153
+ if (transitionRunning.value) return;
146
154
  showSlider('next');
147
155
  };
148
156
 
149
157
  const doPrevious = () => {
150
- if (isLoading.value) return;
158
+ if (transitionRunning.value) return;
151
159
  showSlider('prev');
152
160
  };
153
161
 
@@ -155,6 +163,8 @@ let runTimeOut: any;
155
163
  let runNextAuto: any = null;
156
164
 
157
165
  function showSlider(type: string) {
166
+ transitionRunning.value = true;
167
+
158
168
  const currentSliderItems = Array.from(sliderGalleryImagesList.value?.children || []);
159
169
  const currentThumbnailItems = Array.from(sliderGalleryThumbnailsList.value?.children || []);
160
170
 
@@ -189,31 +199,45 @@ function showSlider(type: string) {
189
199
 
190
200
  clearTimeout(runTimeOut);
191
201
  runTimeOut = setTimeout(() => {
192
- // Your existing cleanup code
202
+
193
203
  if (sliderGalleryWrapper.value) {
194
204
  sliderGalleryWrapper.value.classList.remove('next');
195
205
  sliderGalleryWrapper.value.classList.remove('prev');
196
206
 
197
- // Remove helper classes
198
207
  const items = sliderGalleryImagesList.value?.querySelectorAll('.prepend-item');
199
208
  items?.forEach((item) => item.classList.remove('prepend-item'));
200
209
 
201
210
  const thumbs = sliderGalleryThumbnailsList.value?.querySelectorAll('.prepend-item');
202
211
  thumbs?.forEach((thumb) => thumb.classList.remove('prepend-item'));
203
212
  }
213
+ transitionRunning.value = false;
204
214
  }, props.animationDuration);
205
215
 
206
216
  // Reset auto-run timer
207
217
  clearTimeout(runNextAuto);
208
218
  runNextAuto = setTimeout(() => {
209
- if (!props.autoRun || isLoading.value) return;
219
+ if (!props.autoRun || galleryLoaded.value) return;
210
220
  doNext();
211
221
  }, props.autoRunInterval);
212
222
  }
213
223
 
224
+ // Add keyboard navigation event handlers
225
+ const handleKeyDown = (event: KeyboardEvent) => {
226
+ // Don't process key events if transition is running or gallery isn't loaded
227
+ if (transitionRunning.value || galleryLoaded.value) {
228
+ return;
229
+ }
230
+
231
+ if (event.key === 'ArrowLeft') {
232
+ doPrevious();
233
+ } else if (event.key === 'ArrowRight') {
234
+ doNext();
235
+ }
236
+ };
237
+
214
238
  // Initialize auto-run only after loading completes
215
- watch(isLoading, (isLoadingNow) => {
216
- if (!isLoadingNow && props.autoRun) {
239
+ watch(galleryLoaded, (previousValue, currentValue) => {
240
+ if (!currentValue && props.autoRun) {
217
241
  clearTimeout(runNextAuto);
218
242
  runNextAuto = setTimeout(() => {
219
243
  doNext();
@@ -231,10 +255,7 @@ watch(
231
255
  onBeforeUnmount(() => {
232
256
  clearTimeout(runTimeOut);
233
257
  clearTimeout(runNextAuto);
234
- });
235
-
236
- onMounted(() => {
237
- showGallery.value = true;
258
+ window.removeEventListener('keydown', handleKeyDown);
238
259
  });
239
260
  </script>
240
261
 
@@ -258,6 +279,8 @@ onMounted(() => {
258
279
  position: absolute;
259
280
  inset: 0 0 0 0;
260
281
 
282
+ z-index: 9999;
283
+
261
284
  .loading-state {
262
285
  position: absolute;
263
286
  inset: 0 0 0 0;
@@ -272,7 +295,7 @@ onMounted(() => {
272
295
  transition: display 0.5s, opacity 0.5s;
273
296
  transition-behavior: allow-discrete;
274
297
 
275
- &.loaded {
298
+ &.galleryLoaded {
276
299
  display: none;
277
300
  opacity: 0;
278
301
  }
@@ -300,7 +323,7 @@ onMounted(() => {
300
323
  /* opacity: 0; */
301
324
  /* transition: opacity 0.5s ease-in-out; */
302
325
 
303
- &.loaded {
326
+ &.galleryLoaded {
304
327
  /* opacity: 1; */
305
328
  }
306
329
  }
@@ -457,24 +480,46 @@ onMounted(() => {
457
480
  width: 300px;
458
481
  max-width: 30%;
459
482
  display: flex;
460
- gap: 10px;
483
+ gap: 20px;
461
484
  align-items: center;
462
485
 
463
486
  button {
487
+ display: grid;
488
+ justify-content: center;
489
+ align-items: center;
464
490
  width: 40px;
465
491
  height: 40px;
466
492
  border-radius: 50%;
467
493
  background-color: #eee4;
468
- border: none;
469
494
  color: #fff;
470
495
  font-family: monospace;
471
496
  font-weight: bold;
472
497
  transition: 0.5s;
473
498
 
499
+ border-width: 2px;
500
+ border-style: solid;
501
+ border-color: white;
502
+
503
+ &#prev {
504
+ --_translateX: -2px;
505
+ }
506
+
507
+ &#next {
508
+ --_translateX: 2px;
509
+ }
510
+
474
511
  &:hover {
475
512
  background-color: #fff;
476
513
  color: #000;
477
514
  }
515
+
516
+ .arrows-icon {
517
+ color: currentColor;
518
+ font-weight: 900;
519
+ height: 40px;
520
+ width: 40px;
521
+ translate: var(--_translateX) -3px;
522
+ }
478
523
  }
479
524
  }
480
525
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "2.3.1",
4
+ "version": "2.4.0",
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,18 +27,18 @@
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.3.0",
31
- "@nuxt/eslint-config": "1.2.0",
32
- "@nuxt/icon": "1.11.0",
30
+ "@nuxt/eslint": "1.3.0",
31
+ "@nuxt/eslint-config": "1.3.0",
32
+ "@nuxt/icon": "1.12.0",
33
33
  "@nuxt/image": "1.10.0",
34
34
  "happy-dom": "16.8.1",
35
- "nuxt": "3.16.0",
35
+ "nuxt": "3.17.2",
36
36
  "release-it": "18.1.2",
37
- "typescript": "5.8.2"
37
+ "typescript": "5.8.3"
38
38
  },
39
39
  "dependencies": {
40
- "@oddbird/css-anchor-positioning": "0.4.0",
41
- "@vueuse/core": "13.0.0",
40
+ "@oddbird/css-anchor-positioning": "0.6.0",
41
+ "@vueuse/core": "13.1.0",
42
42
  "focus-trap-vue": "4.0.3",
43
43
  "modern-normalize": "3.0.1"
44
44
  },