diff --git a/playgrounds/nuxt/app/pages/components/marquee.vue b/playgrounds/nuxt/app/pages/components/marquee.vue index 763aa4d0e0..5cc66ff87b 100644 --- a/playgrounds/nuxt/app/pages/components/marquee.vue +++ b/playgrounds/nuxt/app/pages/components/marquee.vue @@ -5,23 +5,35 @@ const orientations = Object.keys(theme.variants.orientation) const orientation = ref('horizontal' as keyof typeof theme.variants.orientation) const pauseOnHover = ref(false) +const pauseOnTouch = ref(false) const reverse = ref(false) const overlay = ref(false) +const repeat = ref(3) +const duration = ref(20) +const pauseOnClick = ref(false) diff --git a/src/runtime/components/Marquee.vue b/src/runtime/components/Marquee.vue index 5c6e3690c2..d8f7ccbd7c 100644 --- a/src/runtime/components/Marquee.vue +++ b/src/runtime/components/Marquee.vue @@ -16,6 +16,11 @@ export interface MarqueeProps { * @defaultValue false */ pauseOnHover?: boolean + /** + * Pause the marquee on touch. + * @defaultValue false + */ + pauseOnTouch?: boolean /** * Reverse the direction of the marquee. * @defaultValue false @@ -36,6 +41,16 @@ export interface MarqueeProps { * @defaultValue true */ overlay?: boolean + /** + * The duration of the marquee animation in seconds. + * @defaultValue 20 + */ + duration?: number + /** + * Pause the marquee when clicking on it. + * @defaultValue false + */ + pauseOnClick?: boolean class?: any ui?: Marquee['slots'] } @@ -46,15 +61,18 @@ export interface MarqueeSlots { diff --git a/src/theme/marquee.ts b/src/theme/marquee.ts index 2b9accee54..93cf33a7ea 100644 --- a/src/theme/marquee.ts +++ b/src/theme/marquee.ts @@ -1,7 +1,8 @@ export default { slots: { - root: 'group relative flex items-center overflow-hidden gap-(--gap) [--gap:--spacing(16)] [--duration:20s]', - content: 'flex items-center shrink-0 justify-around gap-(--gap) min-w-max' + root: 'group relative flex items-center overflow-hidden gap-(--gap) [--gap:--spacing(16)] motion-reduce:animate-none', + content: 'flex items-center shrink-0 justify-around gap-(--gap) min-w-max relative z-0', + overlay: 'absolute inset-0 z-10 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity' }, variants: { orientation: { @@ -17,6 +18,11 @@ export default { content: 'group-hover:[animation-play-state:paused]' } }, + pauseOnTouch: { + true: { + content: 'group-active:[animation-play-state:paused]' + } + }, reverse: { true: { content: '![animation-direction:reverse]' diff --git a/test/components/Marquee.spec.ts b/test/components/Marquee.spec.ts index f9b1e51182..0e6402c882 100644 --- a/test/components/Marquee.spec.ts +++ b/test/components/Marquee.spec.ts @@ -1,6 +1,7 @@ import { describe, it, expect } from 'vitest' import { axe } from 'vitest-axe' import { mountSuspended } from '@nuxt/test-utils/runtime' +import { h } from 'vue' import Marquee from '../../src/runtime/components/Marquee.vue' import type { MarqueeProps, MarqueeSlots } from '../../src/runtime/components/Marquee.vue' import ComponentRender from '../component-render' @@ -23,6 +24,61 @@ describe('Marquee', () => { expect(html).toMatchSnapshot() }) + it('handles pauseOnClick correctly', async () => { + const wrapper = await mountSuspended(Marquee, { + props: { + pauseOnClick: true, + duration: 30 + }, + slots: { + default: () => 'Content' + } + }) + + // Check duration style + expect(wrapper.attributes('style')).toContain('--duration: 30s') + + // Initial state: not paused + expect(wrapper.find('[data-slot="content"]').attributes('style')).toBeUndefined() + + // Check overlay exists and is visible (conditionally based on implementation, here check class or existence) + const overlay = wrapper.find('.absolute.inset-0.z-10') + expect(overlay.exists()).toBe(true) + + // Click wrapper to pause + await wrapper.trigger('click') + + // Paused state + expect(wrapper.find('[data-slot="content"]').attributes('style')).toContain('animation-play-state: paused') + + // Overlay should be gone when paused + expect(wrapper.find('.absolute.inset-0.z-10').exists()).toBe(false) + + // Click wrapper to resume + await wrapper.trigger('click') + expect(wrapper.find('[data-slot="content"]').attributes('style')).toBeUndefined() + }) + + it('prevents pause when clicking interactive elements', async () => { + const wrapper = await mountSuspended(Marquee, { + props: { + pauseOnClick: true, + // Reduce repeat to 1 to simplify finding + repeat: 1 + }, + slots: { + default: () => h('button', { id: 'btn' }, 'Click me') + } + }) + + const button = wrapper.find('#btn') + expect(button.exists()).toBe(true) + await button.trigger('click') + + // Should NOT be paused + expect(wrapper.find('[data-slot="content"]').attributes('style')).toBeUndefined() + }) + it('passes accessibility tests', async () => { const wrapper = await mountSuspended(Marquee, { slots: { diff --git a/test/components/Slideover.spec.ts b/test/components/Slideover.spec.ts index 03257818a9..9645b94c79 100644 --- a/test/components/Slideover.spec.ts +++ b/test/components/Slideover.spec.ts @@ -49,5 +49,6 @@ describe('Slideover', () => { }) expect(await axe(wrapper.element)).toHaveNoViolations() + wrapper.unmount() }) }) diff --git a/test/components/__snapshots__/Marquee-vue.spec.ts.snap b/test/components/__snapshots__/Marquee-vue.spec.ts.snap index e95b8ccb46..c76ac26894 100644 --- a/test/components/__snapshots__/Marquee-vue.spec.ts.snap +++ b/test/components/__snapshots__/Marquee-vue.spec.ts.snap @@ -1,84 +1,93 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Marquee > renders with as correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with class correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with default slot correctly 1`] = ` -"
-
Default slot
-
Default slot
-
Default slot
-
Default slot
+"
+
Default slot
+ + + +
" `; exports[`Marquee > renders with orientation correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with overlay off correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with pauseOnHover correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with repeat correctly 1`] = ` -"
-
-
-
-
-
-
+"
+
+ + + + + +
" `; exports[`Marquee > renders with reverse correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with ui correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; diff --git a/test/components/__snapshots__/Marquee.spec.ts.snap b/test/components/__snapshots__/Marquee.spec.ts.snap index e95b8ccb46..c76ac26894 100644 --- a/test/components/__snapshots__/Marquee.spec.ts.snap +++ b/test/components/__snapshots__/Marquee.spec.ts.snap @@ -1,84 +1,93 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Marquee > renders with as correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with class correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with default slot correctly 1`] = ` -"
-
Default slot
-
Default slot
-
Default slot
-
Default slot
+"
+
Default slot
+ + + +
" `; exports[`Marquee > renders with orientation correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with overlay off correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with pauseOnHover correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with repeat correctly 1`] = ` -"
-
-
-
-
-
-
+"
+
+ + + + + +
" `; exports[`Marquee > renders with reverse correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; exports[`Marquee > renders with ui correctly 1`] = ` -"
-
-
-
-
+"
+
+ + + +
" `; diff --git a/test/components/__snapshots__/PageLogos-vue.spec.ts.snap b/test/components/__snapshots__/PageLogos-vue.spec.ts.snap index 927dc56211..b5d1c13676 100644 --- a/test/components/__snapshots__/PageLogos-vue.spec.ts.snap +++ b/test/components/__snapshots__/PageLogos-vue.spec.ts.snap @@ -42,11 +42,12 @@ exports[`PageLogos > renders with items correctly 1`] = ` exports[`PageLogos > renders with marquee correctly 1`] = ` "
-
-
Benjamin CanacHugo Richard
-
Benjamin CanacHugo Richard
-
Benjamin CanacHugo Richard
-
Benjamin CanacHugo Richard
+
+
Benjamin CanacHugo Richard
+ + + +
" `; diff --git a/test/components/__snapshots__/PageLogos.spec.ts.snap b/test/components/__snapshots__/PageLogos.spec.ts.snap index 927dc56211..b5d1c13676 100644 --- a/test/components/__snapshots__/PageLogos.spec.ts.snap +++ b/test/components/__snapshots__/PageLogos.spec.ts.snap @@ -42,11 +42,12 @@ exports[`PageLogos > renders with items correctly 1`] = ` exports[`PageLogos > renders with marquee correctly 1`] = ` "
-
-
Benjamin CanacHugo Richard
-
Benjamin CanacHugo Richard
-
Benjamin CanacHugo Richard
-
Benjamin CanacHugo Richard
+
+
Benjamin CanacHugo Richard
+ + + +
" `;