nuxt-hero 0.1.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +212 -0
  3. package/dist/module.d.mts +19 -0
  4. package/dist/module.json +12 -0
  5. package/dist/module.mjs +322 -0
  6. package/dist/runtime/assets/hero.css +1 -0
  7. package/dist/runtime/components/navigation/HeroNavigation.d.vue.ts +18 -0
  8. package/dist/runtime/components/navigation/HeroNavigation.vue +68 -0
  9. package/dist/runtime/components/navigation/HeroNavigation.vue.d.ts +18 -0
  10. package/dist/runtime/components/navigation/HeroPagination.d.vue.ts +22 -0
  11. package/dist/runtime/components/navigation/HeroPagination.vue +50 -0
  12. package/dist/runtime/components/navigation/HeroPagination.vue.d.ts +22 -0
  13. package/dist/runtime/components/slider/HeroSlide.d.vue.ts +66 -0
  14. package/dist/runtime/components/slider/HeroSlide.vue +124 -0
  15. package/dist/runtime/components/slider/HeroSlide.vue.d.ts +66 -0
  16. package/dist/runtime/components/slider/index.d.vue.ts +69 -0
  17. package/dist/runtime/components/slider/index.vue +200 -0
  18. package/dist/runtime/components/slider/index.vue.d.ts +69 -0
  19. package/dist/runtime/components/video/HeroSlideVideo.d.vue.ts +39 -0
  20. package/dist/runtime/components/video/HeroSlideVideo.vue +116 -0
  21. package/dist/runtime/components/video/HeroSlideVideo.vue.d.ts +39 -0
  22. package/dist/runtime/components/video/HeroVideoControls.d.vue.ts +12 -0
  23. package/dist/runtime/components/video/HeroVideoControls.vue +87 -0
  24. package/dist/runtime/components/video/HeroVideoControls.vue.d.ts +12 -0
  25. package/dist/runtime/components/video/HeroVideoScrubber.d.vue.ts +64 -0
  26. package/dist/runtime/components/video/HeroVideoScrubber.vue +50 -0
  27. package/dist/runtime/components/video/HeroVideoScrubber.vue.d.ts +64 -0
  28. package/dist/runtime/composables/_autoplay.d.ts +25 -0
  29. package/dist/runtime/composables/_autoplay.js +72 -0
  30. package/dist/runtime/composables/_gsap.d.ts +60 -0
  31. package/dist/runtime/composables/_gsap.js +135 -0
  32. package/dist/runtime/composables/_hls.d.ts +35 -0
  33. package/dist/runtime/composables/_hls.js +88 -0
  34. package/dist/runtime/composables/_slides.d.ts +26 -0
  35. package/dist/runtime/composables/_slides.js +40 -0
  36. package/dist/runtime/composables/_swiper.d.ts +23 -0
  37. package/dist/runtime/composables/_swiper.js +52 -0
  38. package/dist/runtime/composables/_video.d.ts +30 -0
  39. package/dist/runtime/composables/_video.js +89 -0
  40. package/dist/runtime/composables/useHeroSlider.d.ts +24 -0
  41. package/dist/runtime/composables/useHeroSlider.js +131 -0
  42. package/dist/runtime/hero-swiper-modules.d.ts +4 -0
  43. package/dist/runtime/types.d.ts +221 -0
  44. package/dist/runtime/types.js +0 -0
  45. package/dist/runtime/utils.d.ts +22 -0
  46. package/dist/runtime/utils.js +50 -0
  47. package/dist/types.d.mts +9 -0
  48. package/package.json +94 -0
@@ -0,0 +1,69 @@
1
+ import type { HeroSliderProps } from '#hero/types';
2
+ import { patternCSS, patternSize } from '#hero/utils';
3
+ declare var __VLS_30: {
4
+ slide: import("#hero/types").HeroSlide;
5
+ index: number;
6
+ isActive: boolean;
7
+ animationClass: string;
8
+ isVideo: boolean;
9
+ videoPlaying: boolean;
10
+ videoDuration: number;
11
+ videoCurrentTime: number;
12
+ videoWaiting: boolean;
13
+ videoEnded: boolean;
14
+ videoMuted: boolean;
15
+ videoVolume: number;
16
+ videoToggle: () => void;
17
+ videoSeek: (time: number) => void;
18
+ videoSetVolume: (v: number) => void;
19
+ videoToggleMute: () => void;
20
+ }, __VLS_33: any, __VLS_36: {
21
+ patterns: import("#hero/types").OverlayPattern[];
22
+ index: number;
23
+ isActive: boolean;
24
+ patternCSS: typeof patternCSS;
25
+ patternSize: typeof patternSize;
26
+ }, __VLS_38: {
27
+ activeIndex: number;
28
+ snapIndex: number;
29
+ totalSnaps: number;
30
+ total: number;
31
+ progress: number;
32
+ goTo: (index: number) => void;
33
+ vertical: boolean;
34
+ autoplayEnabled: boolean;
35
+ }, __VLS_47: {
36
+ prev: () => void;
37
+ next: () => void;
38
+ activeIndex: number;
39
+ slides: import("#hero/types").HeroSlide[];
40
+ vertical: boolean;
41
+ };
42
+ type __VLS_Slots = {} & {
43
+ slide?: (props: typeof __VLS_30) => any;
44
+ } & {
45
+ 'video-controls'?: (props: typeof __VLS_33) => any;
46
+ } & {
47
+ overlay?: (props: typeof __VLS_36) => any;
48
+ } & {
49
+ pagination?: (props: typeof __VLS_38) => any;
50
+ } & {
51
+ navigation?: (props: typeof __VLS_47) => any;
52
+ };
53
+ declare const __VLS_base: import("vue").DefineComponent<HeroSliderProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<HeroSliderProps> & Readonly<{}>, {
54
+ enterAnimation: string;
55
+ leaveAnimation: string;
56
+ imagePreset: string;
57
+ overlayPatterns: import("#hero/types").OverlayPattern[];
58
+ parallax: import("vue").MaybeRefOrGetter<boolean | import("#hero/types").ParallaxConfig>;
59
+ as: string;
60
+ ui: import("#hero/types").HeroSliderUI;
61
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
62
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
63
+ declare const _default: typeof __VLS_export;
64
+ export default _default;
65
+ type __VLS_WithSlots<T, S> = T & {
66
+ new (): {
67
+ $slots: S;
68
+ };
69
+ };
@@ -0,0 +1,39 @@
1
+ import type { MediaControlsOptions, VideoMediaControls } from '#hero/types';
2
+ interface SlideVideoProps {
3
+ /** Video source URL */
4
+ src: string;
5
+ /** Poster frame */
6
+ poster?: string;
7
+ /** Whether this slide is currently active */
8
+ isActive?: boolean;
9
+ /** Slide index for video registry */
10
+ slideIndex?: number;
11
+ /** Callback when video is ready */
12
+ onVideoReady?: (index: number, controls: VideoMediaControls) => void;
13
+ /** Callback when video is removed */
14
+ onVideoRemoved?: (index: number) => void;
15
+ /** VueUse useMediaControls options */
16
+ mediaControlsOptions?: MediaControlsOptions;
17
+ /** Whether to show video controls. Default: true */
18
+ showVideoControls?: boolean;
19
+ /** Whether the video should loop. Default: false */
20
+ videoLoop?: boolean;
21
+ /** Whether the video should auto-play when active. Default: true */
22
+ autoPlay?: boolean;
23
+ }
24
+ declare const __VLS_export: import("vue").DefineComponent<SlideVideoProps, {
25
+ mediaControls: import("@vueuse/core").UseMediaControlsReturn;
26
+ hlsState: import("#hero/composables/_hls").UseHlsReturn | null;
27
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<SlideVideoProps> & Readonly<{}>, {
28
+ poster: string;
29
+ showVideoControls: boolean;
30
+ isActive: boolean;
31
+ slideIndex: number;
32
+ onVideoReady: (index: number, controls: VideoMediaControls) => void;
33
+ onVideoRemoved: (index: number) => void;
34
+ mediaControlsOptions: MediaControlsOptions;
35
+ videoLoop: boolean;
36
+ autoPlay: boolean;
37
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
38
+ declare const _default: typeof __VLS_export;
39
+ export default _default;
@@ -0,0 +1,116 @@
1
+ <script setup>
2
+ import { computed, onMounted, onUnmounted, ref, useTemplateRef, watch } from "vue";
3
+ import { useMediaControls } from "@vueuse/core";
4
+ import { useRuntimeConfig } from "#imports";
5
+ import { isHlsUrl, getHeroConfig } from "#hero/utils";
6
+ import { useHls } from "#hero/composables/_hls";
7
+ const props = defineProps({
8
+ src: { type: String, required: true },
9
+ poster: { type: String, required: false, default: void 0 },
10
+ isActive: { type: Boolean, required: false, default: false },
11
+ slideIndex: { type: Number, required: false, default: 0 },
12
+ onVideoReady: { type: Function, required: false, default: void 0 },
13
+ onVideoRemoved: { type: Function, required: false, default: void 0 },
14
+ mediaControlsOptions: { type: null, required: false, default: void 0 },
15
+ showVideoControls: { type: Boolean, required: false, default: false },
16
+ videoLoop: { type: Boolean, required: false, default: false },
17
+ autoPlay: { type: Boolean, required: false, default: true }
18
+ });
19
+ const heroConfig = getHeroConfig(useRuntimeConfig());
20
+ const defaultVolume = heroConfig.defaultVolume ?? 0;
21
+ const hlsEnabled = heroConfig.features?.hls ?? false;
22
+ const videoRef = useTemplateRef("videoRef");
23
+ const isBgHls = computed(() => isHlsUrl(props.src));
24
+ const mediaSrc = computed(() => isBgHls.value ? void 0 : props.src);
25
+ const mediaControls = useMediaControls(videoRef, {
26
+ ...props.mediaControlsOptions,
27
+ src: mediaSrc
28
+ });
29
+ const hlsSrc = computed(() => hlsEnabled && isBgHls.value ? props.src : "");
30
+ const hlsState = hlsEnabled ? useHls(videoRef, hlsSrc) : null;
31
+ mediaControls.volume.value = defaultVolume;
32
+ const ended = ref(false);
33
+ onMounted(() => {
34
+ const el = videoRef.value;
35
+ if (!el) return;
36
+ el.addEventListener("ended", () => {
37
+ ended.value = true;
38
+ });
39
+ });
40
+ watch(() => mediaControls.playing.value, (playing) => {
41
+ if (playing) ended.value = false;
42
+ });
43
+ watch(
44
+ () => props.isActive,
45
+ (active) => {
46
+ if (props.slideIndex === void 0 || props.slideIndex === null) return;
47
+ if (active) {
48
+ props.onVideoReady?.(props.slideIndex, { ...mediaControls, ended });
49
+ } else {
50
+ props.onVideoRemoved?.(props.slideIndex);
51
+ }
52
+ },
53
+ { immediate: true }
54
+ );
55
+ onUnmounted(() => {
56
+ if (props.slideIndex !== void 0 && props.slideIndex !== null) {
57
+ props.onVideoRemoved?.(props.slideIndex);
58
+ }
59
+ });
60
+ const mediaReady = ref(false);
61
+ onMounted(() => {
62
+ const el = videoRef.value;
63
+ if (!el) return;
64
+ const onCanPlay = () => {
65
+ mediaReady.value = true;
66
+ if (props.autoPlay && props.isActive) {
67
+ mediaControls.playing.value = true;
68
+ }
69
+ };
70
+ if (el.readyState >= 3) {
71
+ mediaReady.value = true;
72
+ if (props.autoPlay && props.isActive) {
73
+ mediaControls.playing.value = true;
74
+ }
75
+ } else {
76
+ el.addEventListener("canplay", onCanPlay, { once: true });
77
+ }
78
+ });
79
+ watch(
80
+ () => props.isActive,
81
+ (active) => {
82
+ if (!props.autoPlay) return;
83
+ if (active && mediaReady.value) {
84
+ mediaControls.playing.value = true;
85
+ } else if (!active) {
86
+ mediaControls.playing.value = false;
87
+ }
88
+ },
89
+ { immediate: true }
90
+ );
91
+ watch(mediaReady, (ready) => {
92
+ if (ready && props.autoPlay && props.isActive) {
93
+ mediaControls.playing.value = true;
94
+ }
95
+ });
96
+ watch(() => props.src, () => {
97
+ mediaReady.value = false;
98
+ const el = videoRef.value;
99
+ if (!el) return;
100
+ const onCanPlay = () => {
101
+ mediaReady.value = true;
102
+ if (props.autoPlay && props.isActive) {
103
+ mediaControls.playing.value = true;
104
+ }
105
+ };
106
+ el.addEventListener("canplay", onCanPlay, { once: true });
107
+ });
108
+ defineExpose({
109
+ mediaControls,
110
+ hlsState
111
+ });
112
+ </script>
113
+
114
+ <template>
115
+ <video ref="videoRef" muted :loop="videoLoop" playsinline :poster="poster" class="size-full object-cover will-change-transform" />
116
+ </template>
@@ -0,0 +1,39 @@
1
+ import type { MediaControlsOptions, VideoMediaControls } from '#hero/types';
2
+ interface SlideVideoProps {
3
+ /** Video source URL */
4
+ src: string;
5
+ /** Poster frame */
6
+ poster?: string;
7
+ /** Whether this slide is currently active */
8
+ isActive?: boolean;
9
+ /** Slide index for video registry */
10
+ slideIndex?: number;
11
+ /** Callback when video is ready */
12
+ onVideoReady?: (index: number, controls: VideoMediaControls) => void;
13
+ /** Callback when video is removed */
14
+ onVideoRemoved?: (index: number) => void;
15
+ /** VueUse useMediaControls options */
16
+ mediaControlsOptions?: MediaControlsOptions;
17
+ /** Whether to show video controls. Default: true */
18
+ showVideoControls?: boolean;
19
+ /** Whether the video should loop. Default: false */
20
+ videoLoop?: boolean;
21
+ /** Whether the video should auto-play when active. Default: true */
22
+ autoPlay?: boolean;
23
+ }
24
+ declare const __VLS_export: import("vue").DefineComponent<SlideVideoProps, {
25
+ mediaControls: import("@vueuse/core").UseMediaControlsReturn;
26
+ hlsState: import("#hero/composables/_hls").UseHlsReturn | null;
27
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<SlideVideoProps> & Readonly<{}>, {
28
+ poster: string;
29
+ showVideoControls: boolean;
30
+ isActive: boolean;
31
+ slideIndex: number;
32
+ onVideoReady: (index: number, controls: VideoMediaControls) => void;
33
+ onVideoRemoved: (index: number) => void;
34
+ mediaControlsOptions: MediaControlsOptions;
35
+ videoLoop: boolean;
36
+ autoPlay: boolean;
37
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
38
+ declare const _default: typeof __VLS_export;
39
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import type { Ref } from 'vue';
2
+ interface VideoControlsProps {
3
+ playing: Ref<boolean>;
4
+ waiting: Ref<boolean>;
5
+ currentTime: Ref<number>;
6
+ volume: Ref<number>;
7
+ muted: Ref<boolean>;
8
+ duration: Ref<number>;
9
+ }
10
+ declare const __VLS_export: import("vue").DefineComponent<VideoControlsProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<VideoControlsProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
11
+ declare const _default: typeof __VLS_export;
12
+ export default _default;
@@ -0,0 +1,87 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import { formatTime } from "#hero/utils";
4
+ const props = defineProps({
5
+ playing: { type: Object, required: true },
6
+ waiting: { type: Object, required: true },
7
+ currentTime: { type: Object, required: true },
8
+ volume: { type: Object, required: true },
9
+ muted: { type: Object, required: true },
10
+ duration: { type: Object, required: true }
11
+ });
12
+ const formattedTime = computed(
13
+ () => `${formatTime(Number(props.currentTime.value))} / ${formatTime(Number(props.duration.value))}`
14
+ );
15
+ function toggle() {
16
+ props.playing.value = !props.playing.value;
17
+ }
18
+ const progress = computed(() => {
19
+ if (props.duration.value === 0) return 0;
20
+ return props.currentTime.value / props.duration.value * 100;
21
+ });
22
+ const volumePercent = computed(() => Math.round((props.volume.value ?? 0) * 100));
23
+ function onVolumeInput(e) {
24
+ const val = Number(e.target.value);
25
+ props.volume.value = val / 100;
26
+ if (val > 0 && props.muted.value) {
27
+ props.muted.value = false;
28
+ }
29
+ }
30
+ function toggleMute() {
31
+ props.muted.value = !props.muted.value;
32
+ }
33
+ const volumeIcon = computed(() => {
34
+ if (props.muted.value || props.volume.value === 0) return "muted";
35
+ if (props.volume.value < 0.5) return "low";
36
+ return "high";
37
+ });
38
+ </script>
39
+
40
+ <template>
41
+ <div aria-live="polite"
42
+ class="pointer-events-auto absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-10 flex items-center justify-center">
43
+ <button type="button"
44
+ class="inline-flex items-center justify-center size-12 rounded-full text-white cursor-pointer hero-video-btn group"
45
+ :disabled="waiting.value" :aria-label="playing.value ? 'Pause video' : 'Play video'" @click="toggle">
46
+ <span v-if="waiting.value" class="hero-spinner hero-spinner-md text-white" />
47
+ <Icon v-else-if="playing.value" name="lucide:pause"
48
+ class="size-4 transition-transform duration-150 group-hover:scale-110" />
49
+ <Icon v-else name="lucide:play" class="size-4 transition-transform duration-150 group-hover:scale-110" />
50
+
51
+ <div class="absolute flex size-full items-center justify-center">
52
+ <div class="hero-radial-progress size-full m-auto"
53
+ :style="{ '--hero-progress-value': Math.round(progress), '--hero-progress-size': '3rem', '--hero-progress-thickness': '0.25rem' }"
54
+ :aria-valuenow="Math.round(progress)" role="progressbar" />
55
+ </div>
56
+ </button>
57
+ </div>
58
+ <div>
59
+ <div class="media-controls">
60
+ <button type="button"
61
+ class="inline-flex items-center justify-center size-8 rounded mr-1 shadow-none text-white hover:opacity-100 transition-opacity cursor-pointer"
62
+ :aria-label="playing.value ? 'Pause video' : 'Play video'" @click="toggle">
63
+ <span v-if="waiting.value" class="hero-spinner hero-spinner-sm text-white" />
64
+ <Icon v-else-if="playing.value" name="lucide:pause" class="size-4 transition-transform duration-150" />
65
+ <Icon v-else name="lucide:play" class="size-4 transition-transform duration-150" />
66
+ </button>
67
+ <div class="group/volume flex items-center">
68
+ <button type="button"
69
+ class="inline-flex items-center justify-center size-8 rounded mr-1 shadow-none text-white hover:opacity-100 transition-opacity cursor-pointer"
70
+ aria-label="Toggle mute" @click="toggleMute">
71
+ <Icon v-if="volumeIcon === 'high'" name="lucide:volume-2" class="size-4" />
72
+ <Icon v-else-if="volumeIcon === 'low'" name="lucide:volume-1" class="size-4" />
73
+ <Icon v-else name="lucide:volume-x" class="size-4" />
74
+ </button>
75
+ <div class="overflow-hidden flex group-hover/volume:w-24 transition-all duration-300 ease-out">
76
+ <input type="range" min="0" max="100" :value="muted.value ? 0 : volumePercent" class="text-white"
77
+ aria-label="Volume" :aria-valuetext="`${muted.value ? 0 : volumePercent}%`" @input="onVolumeInput" />
78
+ </div>
79
+ </div>
80
+ </div>
81
+ <!-- Time Display -->
82
+ <span
83
+ class="text-xs p-2 mb-4 mr-2 text-center rounded backdrop-blur-sm bg-white/85 min-w-25 self-end bottom-1 right-0 absolute z-2 dark:bg-black/85">
84
+ {{ formattedTime }}
85
+ </span>
86
+ </div>
87
+ </template>
@@ -0,0 +1,12 @@
1
+ import type { Ref } from 'vue';
2
+ interface VideoControlsProps {
3
+ playing: Ref<boolean>;
4
+ waiting: Ref<boolean>;
5
+ currentTime: Ref<number>;
6
+ volume: Ref<number>;
7
+ muted: Ref<boolean>;
8
+ duration: Ref<number>;
9
+ }
10
+ declare const __VLS_export: import("vue").DefineComponent<VideoControlsProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<VideoControlsProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
11
+ declare const _default: typeof __VLS_export;
12
+ export default _default;
@@ -0,0 +1,64 @@
1
+ declare var __VLS_1: {
2
+ pendingValue: number;
3
+ position: string;
4
+ };
5
+ type __VLS_Slots = {} & {
6
+ default?: (props: typeof __VLS_1) => any;
7
+ };
8
+ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
9
+ min: {
10
+ type: NumberConstructor;
11
+ default: number;
12
+ };
13
+ max: {
14
+ type: NumberConstructor;
15
+ default: number;
16
+ };
17
+ secondary: {
18
+ type: NumberConstructor;
19
+ default: number;
20
+ };
21
+ modelValue: {
22
+ type: import("vue").PropType<number>;
23
+ };
24
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
25
+ click: (...args: any[]) => void;
26
+ scrubbing: (...args: any[]) => void;
27
+ scrubberMousedown: (...args: any[]) => void;
28
+ scrubberMouseup: (...args: any[]) => void;
29
+ "update:modelValue": (value: number) => void;
30
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
31
+ min: {
32
+ type: NumberConstructor;
33
+ default: number;
34
+ };
35
+ max: {
36
+ type: NumberConstructor;
37
+ default: number;
38
+ };
39
+ secondary: {
40
+ type: NumberConstructor;
41
+ default: number;
42
+ };
43
+ modelValue: {
44
+ type: import("vue").PropType<number>;
45
+ };
46
+ }>> & Readonly<{
47
+ onClick?: ((...args: any[]) => any) | undefined;
48
+ onScrubbing?: ((...args: any[]) => any) | undefined;
49
+ onScrubberMousedown?: ((...args: any[]) => any) | undefined;
50
+ onScrubberMouseup?: ((...args: any[]) => any) | undefined;
51
+ "onUpdate:modelValue"?: ((value: number) => any) | undefined;
52
+ }>, {
53
+ min: number;
54
+ max: number;
55
+ secondary: number;
56
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
57
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
58
+ declare const _default: typeof __VLS_export;
59
+ export default _default;
60
+ type __VLS_WithSlots<T, S> = T & {
61
+ new (): {
62
+ $slots: S;
63
+ };
64
+ };
@@ -0,0 +1,50 @@
1
+ <script setup>
2
+ import { ref, watch, useTemplateRef } from "vue";
3
+ import { useEventListener, useMouseInElement, onClickOutside } from "@vueuse/core";
4
+ const props = defineProps({
5
+ min: { type: Number, default: 0 },
6
+ max: { type: Number, default: 100 },
7
+ secondary: { type: Number, default: 0 }
8
+ });
9
+ const emit = defineEmits(["scrubbing", "scrubberMousedown", "scrubberMouseup", "click"]);
10
+ const scrubber = useTemplateRef("scrubber");
11
+ const scrubbing = ref(false);
12
+ const pendingValue = ref(0);
13
+ useEventListener("mouseup", () => {
14
+ if (!scrubbing.value) return;
15
+ scrubbing.value = false;
16
+ emit("scrubbing", false);
17
+ emit("scrubberMouseup", true);
18
+ });
19
+ onClickOutside(scrubber, () => {
20
+ if (!scrubbing.value) return;
21
+ scrubbing.value = false;
22
+ emit("scrubbing", false);
23
+ emit("scrubberMouseup", true);
24
+ });
25
+ const value = defineModel({ type: Number, ...{ required: false, default: 0 } });
26
+ const { elementX, elementWidth } = useMouseInElement(scrubber);
27
+ watch([scrubbing, elementX], () => {
28
+ const progress = Math.max(0, Math.min(1, elementX.value / elementWidth.value));
29
+ pendingValue.value = progress * props.max;
30
+ if (scrubbing.value) {
31
+ value.value = pendingValue.value;
32
+ emit("scrubbing", true);
33
+ }
34
+ });
35
+ </script>
36
+
37
+ <template>
38
+ <div ref="scrubber" class="cursor-pointer select-none bg-white/15"
39
+ @mousedown.stop="(scrubbing = true, emit('scrubberMousedown', true))">
40
+ <div class="h-full w-full relative overflow-hidden">
41
+ <div class="bg-white rounded-e-sm h-full w-full left-0 top-0 absolute"
42
+ :style="{ transform: `translateX(${secondary / max * 100 - 100}%)` }" />
43
+ <div class="bg-white h-full w-full relative rounded-e-sm"
44
+ :style="{ transform: `translateX(${value / max * 100 - 100}%)` }" />
45
+ </div>
46
+ <div class="opacity-0 inset-0 absolute hover:opacity-100" :class="{ 'opacity-100': scrubbing }">
47
+ <slot :pending-value="pendingValue" :position="`${Math.max(0, Math.min(elementX, elementWidth))}px`" />
48
+ </div>
49
+ </div>
50
+ </template>
@@ -0,0 +1,64 @@
1
+ declare var __VLS_1: {
2
+ pendingValue: number;
3
+ position: string;
4
+ };
5
+ type __VLS_Slots = {} & {
6
+ default?: (props: typeof __VLS_1) => any;
7
+ };
8
+ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
9
+ min: {
10
+ type: NumberConstructor;
11
+ default: number;
12
+ };
13
+ max: {
14
+ type: NumberConstructor;
15
+ default: number;
16
+ };
17
+ secondary: {
18
+ type: NumberConstructor;
19
+ default: number;
20
+ };
21
+ modelValue: {
22
+ type: import("vue").PropType<number>;
23
+ };
24
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
25
+ click: (...args: any[]) => void;
26
+ scrubbing: (...args: any[]) => void;
27
+ scrubberMousedown: (...args: any[]) => void;
28
+ scrubberMouseup: (...args: any[]) => void;
29
+ "update:modelValue": (value: number) => void;
30
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
31
+ min: {
32
+ type: NumberConstructor;
33
+ default: number;
34
+ };
35
+ max: {
36
+ type: NumberConstructor;
37
+ default: number;
38
+ };
39
+ secondary: {
40
+ type: NumberConstructor;
41
+ default: number;
42
+ };
43
+ modelValue: {
44
+ type: import("vue").PropType<number>;
45
+ };
46
+ }>> & Readonly<{
47
+ onClick?: ((...args: any[]) => any) | undefined;
48
+ onScrubbing?: ((...args: any[]) => any) | undefined;
49
+ onScrubberMousedown?: ((...args: any[]) => any) | undefined;
50
+ onScrubberMouseup?: ((...args: any[]) => any) | undefined;
51
+ "onUpdate:modelValue"?: ((value: number) => any) | undefined;
52
+ }>, {
53
+ min: number;
54
+ max: number;
55
+ secondary: number;
56
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
57
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
58
+ declare const _default: typeof __VLS_export;
59
+ export default _default;
60
+ type __VLS_WithSlots<T, S> = T & {
61
+ new (): {
62
+ $slots: S;
63
+ };
64
+ };
@@ -0,0 +1,25 @@
1
+ import type { ComputedRef, Ref } from 'vue';
2
+ import type { UseHeroSliderOptions } from '#hero/types';
3
+ /**
4
+ * Creates the autoplay timer state. Manages elapsed time, progress,
5
+ * and pause/resume logic. Pauses automatically when a video slide
6
+ * is active or the slider is hovered.
7
+ *
8
+ * @param advanceSlide - Callback to advance to the next slide
9
+ * @param isActiveSlideVideo - Whether the current slide has a video background
10
+ * @param isHovered - Whether the user is hovering over the slider
11
+ * @param options - Swiper options containing autoplay delay config
12
+ * @returns Autoplay state refs and control functions
13
+ */
14
+ export declare function createAutoplayState(advanceSlide: () => void, isActiveSlideVideo: ComputedRef<boolean>, isHovered: Ref<boolean>, options: Pick<UseHeroSliderOptions, 'swiperOptions'>): {
15
+ autoplayEnabled: boolean;
16
+ autoplayProgress: ComputedRef<number>;
17
+ autoplayRemaining: ComputedRef<number>;
18
+ autoplayDelay: Ref<number, number>;
19
+ autoplayPaused: Ref<boolean, boolean>;
20
+ autoplayPause: () => void;
21
+ autoplayResume: () => void;
22
+ autoplayReset: () => void;
23
+ autoplaySetDelay: (ms: number) => void;
24
+ onSlideChange: () => void;
25
+ };
@@ -0,0 +1,72 @@
1
+ import { computed, ref, watch } from "vue";
2
+ import { useIntervalFn } from "@vueuse/core";
3
+ export function createAutoplayState(advanceSlide, isActiveSlideVideo, isHovered, options) {
4
+ const swiperOptions = options.swiperOptions ?? {};
5
+ const autoplayEnabled = swiperOptions.autoplay !== false;
6
+ const initialDelay = swiperOptions.autoplay && typeof swiperOptions.autoplay === "object" ? swiperOptions.autoplay.delay ?? 5e3 : 5e3;
7
+ const TICK_MS = 50;
8
+ const elapsed = ref(0);
9
+ const autoplayDelay = ref(initialDelay);
10
+ const autoplayPaused = ref(!autoplayEnabled);
11
+ const autoplayProgress = computed(() => {
12
+ return Math.min(elapsed.value / autoplayDelay.value, 1);
13
+ });
14
+ const autoplayRemaining = computed(() => {
15
+ return Math.max(autoplayDelay.value - elapsed.value, 0);
16
+ });
17
+ const { pause: pauseTimer, resume: resumeTimer } = useIntervalFn(() => {
18
+ if (isActiveSlideVideo.value || isHovered.value) return;
19
+ elapsed.value += TICK_MS;
20
+ if (elapsed.value >= autoplayDelay.value) {
21
+ advanceSlide();
22
+ elapsed.value = 0;
23
+ }
24
+ }, TICK_MS, { immediate: autoplayEnabled });
25
+ watch(isActiveSlideVideo, (isVideo) => {
26
+ if (!autoplayEnabled) return;
27
+ if (isVideo) {
28
+ pauseTimer();
29
+ } else if (!autoplayPaused.value && !isHovered.value) {
30
+ elapsed.value = 0;
31
+ resumeTimer();
32
+ }
33
+ });
34
+ watch(isHovered, (hovered) => {
35
+ if (!autoplayEnabled) return;
36
+ if (hovered) {
37
+ elapsed.value = 0;
38
+ pauseTimer();
39
+ } else if (!autoplayPaused.value && !isActiveSlideVideo.value) {
40
+ resumeTimer();
41
+ }
42
+ });
43
+ function autoplayPause() {
44
+ autoplayPaused.value = true;
45
+ pauseTimer();
46
+ }
47
+ function autoplayResume() {
48
+ autoplayPaused.value = false;
49
+ resumeTimer();
50
+ }
51
+ function autoplayReset() {
52
+ elapsed.value = 0;
53
+ }
54
+ function autoplaySetDelay(ms) {
55
+ autoplayDelay.value = ms;
56
+ }
57
+ function onSlideChange() {
58
+ elapsed.value = 0;
59
+ }
60
+ return {
61
+ autoplayEnabled,
62
+ autoplayProgress,
63
+ autoplayRemaining,
64
+ autoplayDelay,
65
+ autoplayPaused,
66
+ autoplayPause,
67
+ autoplayResume,
68
+ autoplayReset,
69
+ autoplaySetDelay,
70
+ onSlideChange
71
+ };
72
+ }