Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
17 changes: 17 additions & 0 deletions src/packages/configprovider/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,23 @@ export type NutCSSVariables =
| 'nutuiProgressTextPositionTop'
| 'nutuiProgressTextPositionBottom'
| 'nutuiProgressTextBorderRadius'
| 'nutuiProgressVideoTrackColor'
| 'nutuiProgressVideoFillColor'
| 'nutuiProgressVideoThumbColor'
| 'nutuiProgressVideoHeight'
| 'nutuiProgressVideoActiveHeight'
| 'nutuiProgressVideoPausedHeight'
| 'nutuiProgressVideoThumbWidth'
| 'nutuiProgressVideoThumbHeight'
| 'nutuiProgressVideoThumbRadius'
| 'nutuiProgressVideoThumbActiveWidth'
| 'nutuiProgressVideoThumbActiveHeight'
| 'nutuiProgressVideoThumbActiveRadius'
| 'nutuiProgressVideoThumbPausedWidth'
| 'nutuiProgressVideoThumbPausedHeight'
| 'nutuiProgressVideoThumbPausedRadius'
| 'nutuiProgressVideoOpacityStatic'
| 'nutuiProgressVideoOpacityPaused'
| 'nutuiPaginationColor'
| 'nutuiPaginationLiteColor'
| 'nutuiPaginationFontSize'
Expand Down
105 changes: 103 additions & 2 deletions src/packages/progress/__tests__/progress.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import { fireEvent, render } from '@testing-library/react'
import '@testing-library/jest-dom'

import { Progress } from '../progress'
Expand Down Expand Up @@ -53,7 +53,6 @@ test('should handle animation mode and duration', () => {
'transition: width 500ms ease-in-out'
)

// 测试动画完成回调
rerender(
<Progress
percent={100}
Expand All @@ -66,3 +65,105 @@ test('should handle animation mode and duration', () => {
expect(onActiveEndMock).toHaveBeenCalled()
}, 600)
})

// ==== video mode ====
describe('Progress video mode', () => {
test('renders track/fill/thumb structure with is-static by default', () => {
const { container } = render(<Progress mode="video" percent={40} />)
const root = container.querySelector('.nut-progress--video')
expect(root).toBeTruthy()
expect(root?.classList.contains('is-static')).toBe(true)
expect(container.querySelector('.nut-progress-track')).toBeTruthy()
const fill = container.querySelector('.nut-progress-fill') as HTMLElement
expect(fill.style.width).toBe('40%')
const thumb = container.querySelector('.nut-progress-thumb') as HTMLElement
expect(thumb.style.left).toBe('40%')
})

test('applies is-paused state and renders paused icon', () => {
const { container } = render(
<Progress
mode="video"
percent={50}
status="paused"
pausedIcon={<span data-testid="paused-icon" />}
/>
)
const root = container.querySelector('.nut-progress--video')
expect(root?.classList.contains('is-paused')).toBe(true)
expect(container.querySelector('[data-testid="paused-icon"]')).toBeTruthy()
})

test('hides thumb when showThumb=false', () => {
const { container } = render(
<Progress mode="video" percent={20} showThumb={false} />
)
expect(container.querySelector('.nut-progress-thumb')).toBeNull()
})

test('clamps percent to [min, max]', () => {
const { container, rerender } = render(
<Progress mode="video" percent={-50} />
)
const fill = () =>
container.querySelector('.nut-progress-fill') as HTMLElement
expect(fill().style.width).toBe('0%')
rerender(<Progress mode="video" percent={9999} />)
expect(fill().style.width).toBe('100%')
})

test('drag with mouse triggers onDragStart / onDragEnd and onChange', () => {
const onDragStart = vi.fn()
const onDragEnd = vi.fn()
const onChange = vi.fn()
const { container } = render(
<Progress
mode="video"
percent={0}
draggable
onDragStart={onDragStart}
onDragEnd={onDragEnd}
onChange={onChange}
/>
)
const root = container.querySelector('.nut-progress--video') as HTMLElement
root.getBoundingClientRect = () =>
({
left: 0,
top: 0,
right: 200,
bottom: 10,
width: 200,
height: 10,
x: 0,
y: 0,
toJSON: () => ({}),
}) as DOMRect

fireEvent.mouseDown(root, { clientX: 100 })
expect(onDragStart).toHaveBeenCalled()
expect(onChange).toHaveBeenCalledWith(50)
fireEvent.mouseUp(window, { clientX: 160 })
expect(onDragEnd).toHaveBeenCalledWith(80)
})

test('keyboard ArrowRight increments percent by step', () => {
const onChange = vi.fn()
const { container } = render(
<Progress
mode="video"
percent={50}
draggable
step={5}
onChange={onChange}
/>
)
const root = container.querySelector('.nut-progress--video') as HTMLElement
fireEvent.keyDown(root, { key: 'ArrowRight' })
expect(onChange).toHaveBeenCalledWith(55)
fireEvent.keyDown(root, { key: 'Home' })
expect(onChange).toHaveBeenCalledWith(0)
fireEvent.keyDown(root, { key: 'End' })
expect(onChange).toHaveBeenCalledWith(100)
})
})
6 changes: 6 additions & 0 deletions src/packages/progress/demo.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Demo6 from './demos/taro/demo6'
import Demo7 from './demos/taro/demo7'
import Demo8 from './demos/taro/demo8'
import Demo9 from './demos/taro/demo9'
import Demo10 from './demos/taro/demo10'

const ProgressDemo = () => {
const [translated] = useTranslate({
Expand All @@ -25,6 +26,7 @@ const ProgressDemo = () => {
dynamicChange: '动态改变',
lazy: '延迟加载数据',
activeMode: '设置动画时长与播放方式',
videoBasic: '视频进度条',
},
'zh-TW': {
basic: '基礎用法',
Expand All @@ -36,6 +38,7 @@ const ProgressDemo = () => {
dynamicChange: '動態改變',
lazy: '延遲加載數據',
activeMode: '設置動畫時長與播放方式',
videoBasic: '視頻進度條',
},
'en-US': {
basic: 'Basic Usage',
Expand All @@ -47,6 +50,7 @@ const ProgressDemo = () => {
dynamicChange: 'Dynamic Change',
lazy: 'Delay Time',
activeMode: 'Duration And Animation Mode',
videoBasic: 'Video Progress',
},
})

Expand Down Expand Up @@ -77,6 +81,8 @@ const ProgressDemo = () => {
)}
<View className="h2">{translated.activeMode}</View>
<Demo9 />
<View className="h2">{translated.videoBasic}</View>
<Demo10 />
</ScrollView>
</>
)
Expand Down
6 changes: 6 additions & 0 deletions src/packages/progress/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Demo6 from './demos/h5/demo6'
import Demo7 from './demos/h5/demo7'
import Demo8 from './demos/h5/demo8'
import Demo9 from './demos/h5/demo9'
import Demo10 from './demos/h5/demo10'

const ProgressDemo = () => {
const [translated] = useTranslate({
Expand All @@ -22,6 +23,7 @@ const ProgressDemo = () => {
dynamicChange: '动态改变',
lazy: '延迟加载数据',
activeMode: '设置动画时长与播放方式',
videoBasic: '视频进度条',
},
'zh-TW': {
basic: '基礎用法',
Expand All @@ -33,6 +35,7 @@ const ProgressDemo = () => {
dynamicChange: '動態改變',
lazy: '延遲加載數據',
activeMode: '設置動畫時長與播放方式',
videoBasic: '視頻進度條',
},
'en-US': {
basic: 'Basic Usage',
Expand All @@ -44,6 +47,7 @@ const ProgressDemo = () => {
dynamicChange: 'Dynamic Change',
lazy: 'Delay Time',
activeMode: 'Duration And Animation Mode',
videoBasic: 'Video Progress',
},
})

Expand All @@ -68,6 +72,8 @@ const ProgressDemo = () => {
<Demo8 />
<h2>{translated.activeMode}</h2>
<Demo9 />
<h2>{translated.videoBasic}</h2>
<Demo10 />
</div>
</>
)
Expand Down
47 changes: 47 additions & 0 deletions src/packages/progress/demos/h5/demo10.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { useState } from 'react'
import { Progress, Cell } from '@nutui/nutui-react'

const cardStyle: React.CSSProperties = {
padding: '15px 18px',
background: '#999',
borderRadius: 8,
marginBottom: 12,
}

const progressStyle: React.CSSProperties = {
height: 38,
}

const Demo10 = () => {
const [percent, setPercent] = useState(20)
return (
<Cell.Group>
<Cell style={cardStyle} align="center">
<Progress
mode="video"
status="static"
percent={30}
style={progressStyle}
/>
</Cell>
<Cell style={cardStyle}>
<Progress
mode="video"
status="paused"
percent={50}
style={progressStyle}
/>
</Cell>
<Cell style={cardStyle}>
<Progress
mode="video"
percent={percent}
draggable
onChange={(p) => setPercent(p)}
style={progressStyle}
/>
</Cell>
</Cell.Group>
)
}
export default Demo10
48 changes: 48 additions & 0 deletions src/packages/progress/demos/taro/demo10.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState } from 'react'
import { Progress } from '@nutui/nutui-react-taro'
import { View } from '@tarojs/components'
import { PlayStart } from '@nutui/icons-react-taro'

const cardStyle = {
padding: '16px 20px',
background: '#111',
borderRadius: '8px',
marginBottom: '12px',
}

const labelStyle = {
color: 'rgba(255,255,255,0.6)',
fontSize: '12px',
marginBottom: '8px',
}

const Demo10 = () => {
const [percent, setPercent] = useState(20)
return (
<>
<View style={cardStyle}>
<View style={labelStyle}>默认(静态)</View>
<Progress mode="video" status="static" percent={30} />
</View>
<View style={cardStyle}>
<View style={labelStyle}>视频暂停</View>
<Progress
mode="video"
status="paused"
percent={55}
pausedIcon={<PlayStart color="#fff" width={16} height={16} />}
/>
</View>
<View style={cardStyle}>
<View style={labelStyle}>拖动</View>
<Progress
mode="video"
percent={percent}
draggable
onChange={(p) => setPercent(p)}
/>
</View>
</>
)
}
export default Demo10
41 changes: 41 additions & 0 deletions src/packages/progress/doc.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ import { Progress } from '@nutui/nutui-react'
<CodeBlock src='h5/demo9.tsx'></CodeBlock>
:::

### Video Progress

For video-player-like immersive scenarios. Default colors: track `rgba(255,255,255,0.2)`, fill `rgba(255,255,255,0.7)`, thumb `#FFFFFF`. The demo covers three states: static (dimmed), paused (medium intensity, paired with a play icon), and drag (highlighted, enlarged thumb).

:::demo

<CodeBlock src='h5/demo10.tsx'></CodeBlock>

:::

## Progress

### Props
Expand All @@ -100,6 +110,20 @@ import { Progress } from '@nutui/nutui-react'
| duration | Animation completion time (in milliseconds) | `number` | `30` |
| ariaLabel | AccessibilityLabel | `string` | `-` |
| onActiveEnd | Callback function after animation is completed | `() => void` | `-` |
| mode | Progress bar form; `video` enables the video-player style | `default \| video` | `default` |
| status | External state in video mode (`static` / `paused`); drag state is derived internally | `static \| paused` | `static` |
| draggable | Whether users can drag to seek in video mode | `boolean` | `false` |
| showThumb | Whether the draggable thumb is visible in video mode | `boolean` | `true` |
| pausedIcon | Icon rendered when `status='paused'` | `ReactNode` | `-` |
| min | Minimum progress value | `number` | `0` |
| max | Maximum progress value | `number` | `100` |
| step | Step size for drag / keyboard | `number` | `-` |
| onChange | Fires when the progress value changes | `(value: number) => void` | `-` |
| onDragStart | Fires on drag start | `(value: number) => void` | `-` |
| onDragging | Fires while dragging | `(value: number) => void` | `-` |
| onDragEnd | Fires on drag end | `(value: number) => void` | `-` |

> Note: when `mode='video'`, `color / background / strokeWidth` are ignored. Customize the look via the CSS variables below or via the `pausedIcon` slot.

## Theming

Expand All @@ -120,5 +144,22 @@ The component provides the following CSS variables, which can be used to customi
| \--nutui-progress-text-position-bottom | text bottom | `-4px` |
| \--nutui-progress-text-border-radius | text borderRadius | `5px` |
| \--nutui-progress-text-background | text background | `$progress-color` |
| \--nutui-progress-video-track-color | video mode track color | `rgba(255, 255, 255, 0.2)` |
| \--nutui-progress-video-fill-color | video mode filled color | `rgba(255, 255, 255, 0.7)` |
| \--nutui-progress-video-thumb-color | video mode thumb color | `#ffffff` |
| \--nutui-progress-video-height | video mode track height | `1px` |
| \--nutui-progress-video-active-height | track height on drag | `8px` |
| \--nutui-progress-video-paused-height | track height in paused state | `3px` |
| \--nutui-progress-video-thumb-width | video mode thumb width | `2px` |
| \--nutui-progress-video-thumb-height | video mode thumb height | `1px` |
| \--nutui-progress-video-thumb-radius | video mode thumb corner radius | `0.5px` |
| \--nutui-progress-video-thumb-active-width | thumb width on drag | `6px` |
| \--nutui-progress-video-thumb-active-height | thumb height on drag | `12px` |
| \--nutui-progress-video-thumb-active-radius | thumb corner radius on drag | `3px` |
| \--nutui-progress-video-thumb-paused-width | thumb width in paused state | `6px` |
| \--nutui-progress-video-thumb-paused-height | thumb height in paused state | `3px` |
| \--nutui-progress-video-thumb-paused-radius | thumb corner radius in paused state | `1.5px` |
| \--nutui-progress-video-opacity-static | opacity in static state | `0.6` |
| \--nutui-progress-video-opacity-paused | opacity in paused state | `0.85` |

<Contribution name="Progress" />
Loading
Loading