Skip to content

Commit 7588105

Browse files
committed
feat: marquee
1 parent 7ecd26b commit 7588105

File tree

102 files changed

+2048
-47
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+2048
-47
lines changed

.storybook/styles.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
@import url('./styles/password-input.css');
2323
@import url('./styles/pin-input.css');
2424
@import url('./styles/popover.css');
25+
@import url('./styles/marquee.css');
2526
@import url('./styles/presence.css');
2627
@import url('./styles/progress.css');
2728
@import url('./styles/qr-code.css');

.storybook/styles/marquee.css

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
@keyframes marqueeX {
2+
from {
3+
transform: translateX(0%);
4+
}
5+
to {
6+
transform: translateX(var(--marquee-translate));
7+
}
8+
}
9+
10+
@keyframes marqueeY {
11+
from {
12+
transform: translateY(0%);
13+
}
14+
to {
15+
transform: translateY(var(--marquee-translate));
16+
}
17+
}
18+
19+
[data-scope='marquee'][data-part='root'] {
20+
position: relative;
21+
width: 100%;
22+
max-width: 100%;
23+
}
24+
25+
[data-scope='marquee'][data-part='root'][data-paused] {
26+
animation-play-state: paused !important;
27+
}
28+
29+
[data-scope='marquee'][data-part='root'][data-paused] * {
30+
animation-play-state: paused !important;
31+
}
32+
33+
[data-scope='marquee'][data-part='viewport'] {
34+
overflow: hidden;
35+
display: flex;
36+
}
37+
38+
[data-scope='marquee'][data-part='content'] {
39+
display: flex;
40+
min-width: max-content;
41+
animation-timing-function: linear;
42+
animation-fill-mode: forwards;
43+
animation-duration: var(--marquee-duration);
44+
animation-delay: var(--marquee-delay);
45+
animation-iteration-count: var(--marquee-loop-count);
46+
}
47+
48+
[data-scope='marquee'][data-part='content'][data-side='start'],
49+
[data-scope='marquee'][data-part='content'][data-side='end'] {
50+
animation-name: marqueeX;
51+
}
52+
53+
[data-scope='marquee'][data-part='content'][data-side='top'],
54+
[data-scope='marquee'][data-part='content'][data-side='bottom'] {
55+
animation-name: marqueeY;
56+
}
57+
58+
[data-scope='marquee'][data-part='content'][data-reverse] {
59+
animation-direction: reverse;
60+
}
61+
62+
@media (prefers-reduced-motion: reduce) {
63+
[data-scope='marquee'][data-part='content'] {
64+
animation: none !important;
65+
}
66+
}
67+
68+
[data-scope='marquee'][data-part='content'][data-orientation='horizontal'] {
69+
flex-direction: row;
70+
}
71+
72+
[data-scope='marquee'][data-part='content'][data-orientation='vertical'] {
73+
flex-direction: column;
74+
}
75+
76+
[data-scope='marquee'][data-part='edge'] {
77+
position: absolute;
78+
z-index: 10;
79+
pointer-events: none;
80+
}
81+
82+
[data-scope='marquee'][data-part='edge'][data-side='start'] {
83+
width: 20%;
84+
background: linear-gradient(to right, white, transparent);
85+
}
86+
87+
[data-scope='marquee'][data-part='edge'][data-side='start'][dir='rtl'] {
88+
background: linear-gradient(to left, white, transparent);
89+
}
90+
91+
[data-scope='marquee'][data-part='edge'][data-side='end'] {
92+
width: 20%;
93+
background: linear-gradient(to left, white, transparent);
94+
}
95+
96+
[data-scope='marquee'][data-part='edge'][data-side='end'][dir='rtl'] {
97+
background: linear-gradient(to right, white, transparent);
98+
}
99+
100+
[data-scope='marquee'][data-part='edge'][data-side='top'] {
101+
height: 20%;
102+
background: linear-gradient(to bottom, white, transparent);
103+
}
104+
105+
[data-scope='marquee'][data-part='edge'][data-side='bottom'] {
106+
height: 20%;
107+
background: linear-gradient(to top, white, transparent);
108+
}

.storybook/styles/sizer.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
[data-scope='sizer'][data-part='root'] {
2-
border: 2px solid #e2e8f0;
2+
border: 1px solid #e2e8f0;
33
border-radius: 8px;
44
overflow: hidden;
55
}
66

77
[data-scope='sizer'][data-part='content'] {
8-
padding: 1rem;
98
min-width: 300px;
109
}
1110

bun.lock

Lines changed: 32 additions & 26 deletions
Large diffs are not rendered by default.

packages/react/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
"@zag-js/i18n-utils": "1.26.4",
116116
"@zag-js/json-tree-utils": "1.26.4",
117117
"@zag-js/listbox": "1.26.4",
118+
"@zag-js/marquee": "1.26.4",
118119
"@zag-js/menu": "1.26.4",
119120
"@zag-js/number-input": "1.26.4",
120121
"@zag-js/pagination": "1.26.4",
@@ -149,8 +150,8 @@
149150
"devDependencies": {
150151
"check-password-strength": "3.0.0",
151152
"@biomejs/biome": "2.2.6",
152-
"@storybook/addon-a11y": "9.1.13",
153-
"@storybook/react-vite": "9.1.13",
153+
"@storybook/addon-a11y": "9.1.15",
154+
"@storybook/react-vite": "9.1.15",
154155
"@testing-library/dom": "10.4.1",
155156
"@testing-library/jest-dom": "6.9.1",
156157
"@testing-library/react": "16.3.0",
@@ -171,7 +172,7 @@
171172
"react-frame-component": "5.2.7",
172173
"react-hook-form": "7.65.0",
173174
"resize-observer-polyfill": "1.5.1",
174-
"storybook": "9.1.13",
175+
"storybook": "9.1.15",
175176
"typescript": "5.9.3",
176177
"vite": "7.1.12",
177178
"vite-plugin-dts": "4.5.4",

packages/react/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export * from './highlight'
2525
export * from './hover-card'
2626
export * from './json-tree-view'
2727
export * from './listbox'
28+
export * from './marquee'
2829
export * from './menu'
2930
export * from './number-input'
3031
export * from './pagination'
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Marquee } from '@ark-ui/react/marquee'
2+
3+
const items = ['Apple', 'Banana', 'Cherry']
4+
5+
export const AutoFill = () => (
6+
<Marquee.Root autoFill spacing="2rem">
7+
<Marquee.Viewport>
8+
<Marquee.Content>
9+
{items.map((item, i) => (
10+
<div key={i} style={{ padding: '0 2rem' }}>
11+
{item}
12+
</div>
13+
))}
14+
</Marquee.Content>
15+
</Marquee.Viewport>
16+
</Marquee.Root>
17+
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Marquee } from '@ark-ui/react/marquee'
2+
3+
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
4+
5+
export const Basic = () => (
6+
<Marquee.Root>
7+
<Marquee.Viewport>
8+
<Marquee.Content>
9+
{items.map((item, i) => (
10+
<div key={i} style={{ padding: '0 2rem' }}>
11+
{item}
12+
</div>
13+
))}
14+
</Marquee.Content>
15+
</Marquee.Viewport>
16+
</Marquee.Root>
17+
)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useState } from 'react'
2+
import { Marquee } from '@ark-ui/react/marquee'
3+
4+
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
5+
6+
export const FiniteLoops = () => {
7+
const [loopCount, setLoopCount] = useState(0)
8+
const [completedCount, setCompletedCount] = useState(0)
9+
10+
return (
11+
<>
12+
<Marquee.Root
13+
loopCount={3}
14+
onLoopComplete={() => setLoopCount((prev) => prev + 1)}
15+
onComplete={() => setCompletedCount((prev) => prev + 1)}
16+
>
17+
<Marquee.Viewport>
18+
<Marquee.Content>
19+
{items.map((item, i) => (
20+
<div key={i} style={{ padding: '0 2rem' }}>
21+
{item}
22+
</div>
23+
))}
24+
</Marquee.Content>
25+
</Marquee.Viewport>
26+
</Marquee.Root>
27+
28+
<div style={{ marginTop: '1rem' }}>
29+
<p>Loop completed: {loopCount} times</p>
30+
<p>Animation completed: {completedCount} times</p>
31+
</div>
32+
</>
33+
)
34+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Marquee } from '@ark-ui/react/marquee'
2+
3+
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
4+
5+
export const PauseOnInteraction = () => (
6+
<Marquee.Root pauseOnInteraction>
7+
<Marquee.Viewport>
8+
<Marquee.Content>
9+
{items.map((item, i) => (
10+
<div key={i} style={{ padding: '0 2rem' }}>
11+
{item}
12+
</div>
13+
))}
14+
</Marquee.Content>
15+
</Marquee.Viewport>
16+
</Marquee.Root>
17+
)

0 commit comments

Comments
 (0)