Skip to content

Commit 3ae1772

Browse files
committed
fix(vue): marquee content
1 parent 569d51c commit 3ae1772

File tree

5 files changed

+103
-1
lines changed

5 files changed

+103
-1
lines changed

packages/vue/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
- **Combobox**:
2020
- Fixed focus stealing in controlled open mode
2121
- Removed problematic `aria-hidden` behavior to allow interaction with other page elements
22+
- **Marquee**: Fixed `Marquee.Content` not receiving `data-v-*` attribute for scoped styles when using `v-for` at the
23+
root level.
2224

2325
## [5.27.1] - 2025-11-02
2426

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script setup lang="ts">
2+
import { Marquee } from '@ark-ui/vue/marquee'
3+
4+
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
5+
</script>
6+
7+
<template>
8+
<Marquee.Root :auto-fill="true">
9+
<Marquee.Viewport>
10+
<Marquee.Content class="marquee-content">
11+
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">
12+
{{ item }}
13+
</Marquee.Item>
14+
</Marquee.Content>
15+
</Marquee.Viewport>
16+
</Marquee.Root>
17+
</template>
18+
19+
<style scoped>
20+
/* This should work with scoped styles - the data-v-* attribute should be on each content element */
21+
.marquee-content {
22+
background: blue;
23+
color: white;
24+
padding: 0.5rem;
25+
}
26+
27+
.marquee-content:hover {
28+
background: darkblue;
29+
}
30+
</style>

packages/vue/src/components/marquee/marquee-content.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,31 @@ export interface MarqueeContentProps
1212
</script>
1313

1414
<script setup lang="ts">
15+
import { useAttrs } from 'vue'
1516
import { ark } from '../factory'
1617
import { useForwardExpose } from '../../utils/use-forward-expose'
18+
import { useScopeId } from '../../utils/use-scope-id'
1719
import { useMarqueeContext } from './use-marquee-context'
1820
21+
defineOptions({ inheritAttrs: false })
1922
defineProps<MarqueeContentProps>()
23+
2024
const marquee = useMarqueeContext()
25+
const attrs = useAttrs()
26+
const scopeId = useScopeId()
27+
2128
useForwardExpose()
2229
</script>
2330

2431
<template>
2532
<ark.div
2633
v-for="(_, index) in Array.from({ length: marquee.contentCount })"
2734
:key="index"
28-
v-bind="marquee.getContentProps({ index })"
35+
v-bind="{
36+
...attrs,
37+
...(scopeId ? { [scopeId]: '' } : {}),
38+
...marquee.getContentProps({ index }),
39+
}"
2940
:as-child="asChild"
3041
>
3142
<slot />

packages/vue/src/components/marquee/marquee.stories.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import FiniteLoopsExample from './examples/finite-loops.vue'
66
import PauseOnInteractionExample from './examples/pause-on-interaction.vue'
77
import ProgrammaticControlExample from './examples/programmatic-control.vue'
88
import ReverseExample from './examples/reverse.vue'
9+
import ScopedStylesExample from './examples/scoped-styles.vue'
910
import SpeedExample from './examples/speed.vue'
1011
import VerticalExample from './examples/vertical.vue'
1112
import WithEdgesExample from './examples/with-edges.vue'
@@ -58,6 +59,13 @@ export const Reverse = {
5859
}),
5960
}
6061

62+
export const ScopedStyles = {
63+
render: () => ({
64+
components: { Component: ScopedStylesExample },
65+
template: '<Component />',
66+
}),
67+
}
68+
6169
export const Speed = {
6270
render: () => ({
6371
components: { Component: SpeedExample },
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { hasProp, isString } from '@zag-js/utils'
2+
import { getCurrentInstance, type ComponentInternalInstance } from 'vue'
3+
4+
function hasVnodeScopeId(
5+
vnode: ComponentInternalInstance['vnode'],
6+
): vnode is ComponentInternalInstance['vnode'] & { scopeId: string } {
7+
if (vnode === null || typeof vnode !== 'object') return false
8+
const vnodeObj = vnode as unknown as Record<string, unknown>
9+
return hasProp(vnodeObj, 'scopeId') && isString(vnodeObj.scopeId)
10+
}
11+
12+
function hasTypeScopeId(
13+
type: ComponentInternalInstance['type'],
14+
): type is ComponentInternalInstance['type'] & { __scopeId: string } {
15+
if (type === null || typeof type !== 'object') return false
16+
const typeObj = type as unknown as Record<string, unknown>
17+
return hasProp(typeObj, '__scopeId') && isString(typeObj.__scopeId)
18+
}
19+
20+
/**
21+
* Get scope ID from a component instance.
22+
*/
23+
function getScopeIdFromInstance(instance: ComponentInternalInstance): string | undefined {
24+
// Check vnode.scopeId first (Vue 3 stores it here)
25+
if (hasVnodeScopeId(instance.vnode)) return instance.vnode.scopeId
26+
// If not found, check type.__scopeId
27+
if (hasTypeScopeId(instance.type)) return instance.type.__scopeId
28+
}
29+
30+
/**
31+
* Access scope ID for scoped styles from parent component.
32+
* We need to traverse up the component tree to find a parent with scoped styles.
33+
*/
34+
export function useScopeId(): string | undefined {
35+
const instance = getCurrentInstance()
36+
if (!instance) return
37+
38+
// Check current instance first
39+
let scopeId = getScopeIdFromInstance(instance)
40+
41+
// If still not found, check parent components
42+
if (!scopeId && instance.parent) {
43+
let parent: ComponentInternalInstance | null = instance.parent
44+
while (parent && !scopeId) {
45+
scopeId = getScopeIdFromInstance(parent)
46+
parent = parent.parent
47+
}
48+
}
49+
50+
return scopeId
51+
}

0 commit comments

Comments
 (0)