Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/salty-toys-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ultraviolet/ui": minor
---

Refactor `<Carousel />` to use vanilla extract
Original file line number Diff line number Diff line change
Expand Up @@ -2,129 +2,66 @@

exports[`carousel > renders correctly with default props 1`] = `
<DocumentFragment>
.emotion-0 {
position: relative;
margin-left: -100px;
margin-right: -100px;
}

.emotion-2 {
position: absolute;
width: 100px;
height: 100%;
content: '';
background: linear-gradient(
-90deg,
#ffffffff,
#ffffff
);
cursor: w-resize;
z-index: auto;
}

.emotion-4 {
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 0 100px;
gap: 1rem;
}

.emotion-6 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: stretch;
-webkit-box-align: stretch;
-ms-flex-align: stretch;
align-items: stretch;
width: 240px;
max-width: 240px;
overflow-wrap: break-word;
white-space: normal;
height: auto;
cursor: -webkit-grab;
cursor: grab;
-webkit-flex-shrink: 0;
-ms-flex-negative: 0;
flex-shrink: 0;
}

.emotion-18 {
position: absolute;
bottom: 0;
right: 0;
width: 100px;
height: 100%;
content: '';
cursor: e-resize;
z-index: auto;
background: linear-gradient(
-90deg,
#ffffff,
#ffffffff
);
}

<div
<div
data-testid="testing"
>
<div
class="emotion-0 emotion-1"
class="styles__ztme1b1"
data-testid="scrollbar"
>
<span
class="emotion-2 emotion-3"
class="styles__ztme1b2"
data-testid="scrollbar-before"
/>
<div
class="emotion-4 emotion-5"
class="styles__ztme1b3"
data-testid="scrollbar-wrapper"
>
<div
class="emotion-6 emotion-7"
class="styles__ztme1b5"
draggable="true"
style="--ztme1b0: 240px;"
>
Item 1
</div>
<div
class="emotion-6 emotion-7"
class="styles__ztme1b5"
draggable="true"
style="--ztme1b0: 240px;"
>
Item 2
</div>
<div
class="emotion-6 emotion-7"
class="styles__ztme1b5"
draggable="true"
style="--ztme1b0: 240px;"
>
Item 3
</div>
<div
class="emotion-6 emotion-7"
class="styles__ztme1b5"
draggable="true"
style="--ztme1b0: 240px;"
>
Item 4
</div>
<div
class="emotion-6 emotion-7"
class="styles__ztme1b5"
draggable="true"
style="--ztme1b0: 240px;"
>
Item 5
</div>
<div
class="emotion-6 emotion-7"
class="styles__ztme1b5"
draggable="true"
style="--ztme1b0: 240px;"
>
Item 6
</div>
</div>
<span
class="emotion-18 emotion-19"
class="styles__ztme1b4"
data-testid="scrollbar-after"
/>
</div>
Expand Down
100 changes: 31 additions & 69 deletions packages/ui/src/components/Carousel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,16 @@
'use client'

import styled from '@emotion/styled'
import { assignInlineVars } from '@vanilla-extract/dynamic'
import type { ReactNode } from 'react'
import { useEffect, useRef, useState } from 'react'

const StyledWrapper = styled.div`
position: relative;
margin-left: -100px;
margin-right: -100px;
`

const StyledBeforeScroll = styled.span`
position: absolute;
width: 100px;
height: 100%;
content: '';
background: linear-gradient(
-90deg,
${({ theme }) => theme.colors.neutral.background}ff,
${({ theme }) => theme.colors.neutral.background}
);
cursor: w-resize;
z-index: auto;
`

const StyledScrollableWrapper = styled.div`
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
display: flex;
padding: 0 100px;
gap: ${({ theme }) => theme.space['2']};
`

const StyledAfterScroll = styled.span`
position: absolute;
bottom: 0;
right: 0;
width: 100px;
height: 100%;
content: '';
cursor: e-resize;
z-index: auto;
background: linear-gradient(
-90deg,
${({ theme }) => theme.colors.neutral.background},
${({ theme }) => theme.colors.neutral.background}ff
);
`

const StyledBorderWrapper = styled('div', {
shouldForwardProp: prop => !['width'].includes(prop),
})<{ width: string }>`
display: flex;
align-items: stretch;
width: ${({ width }) => width};
max-width: ${({ width }) => width};
overflow-wrap: break-word;
white-space: normal;
height: auto;
cursor: grab;
flex-shrink: 0;
`
import {
afterScroll,
beforeScroll,
borderWrapper,
scrollableWrapper,
widthVar,
wrapper,
} from './styles.css'

type CarouselItemProps = {
children: ReactNode
Expand All @@ -71,9 +20,15 @@ export const CarouselItem = ({
children,
width = '240px',
}: CarouselItemProps) => (
<StyledBorderWrapper draggable="true" width={width}>
<div
className={borderWrapper}
draggable="true"
style={assignInlineVars({
[widthVar]: width,
})}
>
{children}
</StyledBorderWrapper>
</div>
)

type CarouselProps = {
Expand Down Expand Up @@ -125,14 +80,19 @@ export const Carousel = ({
const [deltaX, setDeltaX] = useState(0)

return (
<StyledWrapper className={className} data-testid={dataTestId}>
<StyledBeforeScroll
<div
className={`${className ? `${className} ` : ''}${wrapper}`}
data-testid={dataTestId}
>
<span
className={beforeScroll}
data-testid={`${dataTestId}-before`}
onFocus={handleScrollRight}
onMouseLeave={() => clearInterval(intervalRight)}
onMouseOver={handleScrollRight}
/>
<StyledScrollableWrapper
className={className}
<div
className={`${className ? `${className} ` : ''}${scrollableWrapper}`}
data-testid={`${dataTestId}-wrapper`}
onDrag={() => handleScrollX(deltaX)}
onDragEnd={() => {
Expand All @@ -158,14 +118,16 @@ export const Carousel = ({
ref={scrollRef}
>
{children}
</StyledScrollableWrapper>
</div>

<StyledAfterScroll
<span
className={afterScroll}
data-testid={`${dataTestId}-after`}
onFocus={handleScrollLeft}
onMouseLeave={() => clearInterval(intervalLeft)}
onMouseOver={handleScrollLeft}
/>
</StyledWrapper>
</div>
)
}

Expand Down
53 changes: 53 additions & 0 deletions packages/ui/src/components/Carousel/styles.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { createVar, style } from '@vanilla-extract/css'
import { theme } from '@ultraviolet/themes'

export const widthVar = createVar()

export const wrapper = style({
position: 'relative',
marginLeft: '-100px',
marginRight: '-100px',
})

export const beforeScroll = style({
position: 'absolute',
width: '100px',
height: '100%',
content: "''",
background: `linear-gradient(-90deg, ${theme.colors.neutral.background}ff, ${theme.colors.neutral.background})`,
cursor: 'w-resize',
zIndex: 'auto',
})

export const scrollableWrapper = style({
overflowX: 'scroll',
overflowY: 'hidden',
whiteSpace: 'nowrap',
display: 'flex',
padding: '0 100px',
gap: theme.space['2'],
})

export const afterScroll = style({
position: 'absolute',
bottom: '0',
right: '0',
width: '100px',
height: '100%',
content: "''",
cursor: 'e-resize',
zIndex: 'auto',
background: `linear-gradient(-90deg, ${theme.colors.neutral.background}, ${theme.colors.neutral.background}ff)`,
})

export const borderWrapper = style({
display: 'flex',
alignItems: 'stretch',
width: widthVar,
maxWidth: widthVar,
overflowWrap: 'break-word',
whiteSpace: 'normal',
height: 'auto',
cursor: 'grab',
flexShrink: '0',
})
Loading