Browse Source

Merge branch 'main' into css-way

DaiQiangReal 4 months ago
parent
commit
aaefc1eff3
87 changed files with 4623 additions and 351 deletions
  1. 1 0
      .github/workflows/cypress.yml
  2. 543 1
      content/basic/icon/index-en-US.md
  3. 542 0
      content/basic/icon/index.md
  4. 1 0
      content/order.js
  5. 321 0
      content/plus/videoPlayer/index-en-US.md
  6. 324 0
      content/plus/videoPlayer/index.md
  7. 1 1
      content/show/cropper/index-en-US.md
  8. 1 1
      content/show/cropper/index.md
  9. 1 0
      content/show/table/index-en-US.md
  10. 1 0
      content/show/table/index.md
  11. 24 0
      content/start/changelog/index-en-US.md
  12. 24 0
      content/start/changelog/index.md
  13. 2 1
      content/start/overview/index.md
  14. 58 0
      cypress/e2e/videoPlayer.spec.js
  15. 1 1
      lerna.json
  16. 3 3
      packages/semi-animation-react/package.json
  17. 1 1
      packages/semi-animation-styled/package.json
  18. 1 1
      packages/semi-animation/package.json
  19. 1 1
      packages/semi-eslint-plugin/package.json
  20. 5 1
      packages/semi-foundation/button/button.scss
  21. 4 3
      packages/semi-foundation/button/variables.scss
  22. 2 2
      packages/semi-foundation/cascader/foundation.ts
  23. 9 0
      packages/semi-foundation/datePicker/datePicker.scss
  24. 43 8
      packages/semi-foundation/input/foundation.ts
  25. 45 8
      packages/semi-foundation/input/textareaFoundation.ts
  26. 3 3
      packages/semi-foundation/package.json
  27. 4 1
      packages/semi-foundation/tooltip/foundation.ts
  28. 7 0
      packages/semi-foundation/videoPlayer/animation.scss
  29. 39 0
      packages/semi-foundation/videoPlayer/constants.ts
  30. 332 0
      packages/semi-foundation/videoPlayer/foundation.ts
  31. 136 0
      packages/semi-foundation/videoPlayer/progressFoundation.ts
  32. 75 0
      packages/semi-foundation/videoPlayer/variables.scss
  33. 323 0
      packages/semi-foundation/videoPlayer/videoPlayer.scss
  34. 1 1
      packages/semi-icons-lab/package.json
  35. 1 1
      packages/semi-icons/package.json
  36. 24 0
      packages/semi-icons/src/icons/IconMiniPlayer.tsx
  37. 1 0
      packages/semi-icons/src/icons/index.ts
  38. 1 1
      packages/semi-illustrations/package.json
  39. 1 1
      packages/semi-json-viewer-core/package.json
  40. 2 2
      packages/semi-next/package.json
  41. 1 1
      packages/semi-rspack/package.json
  42. 1 1
      packages/semi-scss-compile/package.json
  43. 1 1
      packages/semi-theme-default/package.json
  44. 96 0
      packages/semi-ui/cascader/_story/cascader.stories.jsx
  45. 2 2
      packages/semi-ui/form/interface.ts
  46. 1 0
      packages/semi-ui/index.ts
  47. 0 1
      packages/semi-ui/input/_story/input.stories.jsx
  48. 2 0
      packages/semi-ui/input/index.tsx
  49. 2 0
      packages/semi-ui/input/textarea.tsx
  50. 11 0
      packages/semi-ui/locale/interface.ts
  51. 11 0
      packages/semi-ui/locale/source/ar.ts
  52. 11 0
      packages/semi-ui/locale/source/de.ts
  53. 11 0
      packages/semi-ui/locale/source/en_GB.ts
  54. 11 0
      packages/semi-ui/locale/source/en_US.ts
  55. 11 0
      packages/semi-ui/locale/source/es.ts
  56. 11 0
      packages/semi-ui/locale/source/fr.ts
  57. 11 0
      packages/semi-ui/locale/source/id_ID.ts
  58. 11 0
      packages/semi-ui/locale/source/it.ts
  59. 11 0
      packages/semi-ui/locale/source/ja_JP.ts
  60. 11 0
      packages/semi-ui/locale/source/ko_KR.ts
  61. 11 0
      packages/semi-ui/locale/source/ms_MY.ts
  62. 11 0
      packages/semi-ui/locale/source/nl_NL.ts
  63. 11 0
      packages/semi-ui/locale/source/pl_PL.ts
  64. 11 0
      packages/semi-ui/locale/source/pt_BR.ts
  65. 11 0
      packages/semi-ui/locale/source/ro.ts
  66. 11 0
      packages/semi-ui/locale/source/ru_RU.ts
  67. 11 0
      packages/semi-ui/locale/source/sv_SE.ts
  68. 11 0
      packages/semi-ui/locale/source/th_TH.ts
  69. 11 0
      packages/semi-ui/locale/source/tr_TR.ts
  70. 11 0
      packages/semi-ui/locale/source/vi_VN.ts
  71. 11 0
      packages/semi-ui/locale/source/zh_CN.ts
  72. 11 0
      packages/semi-ui/locale/source/zh_TW.ts
  73. 7 7
      packages/semi-ui/package.json
  74. 19 0
      packages/semi-ui/select/_story/select.stories.jsx
  75. 4 1
      packages/semi-ui/select/index.tsx
  76. 1 0
      packages/semi-ui/select/utils.tsx
  77. 14 6
      packages/semi-ui/tooltip/index.tsx
  78. 1 1
      packages/semi-ui/tsconfig.json
  79. 15 0
      packages/semi-ui/videoPlayer/ErrorSvg.tsx
  80. 271 0
      packages/semi-ui/videoPlayer/_story/videoPlayer.stories.jsx
  81. 519 0
      packages/semi-ui/videoPlayer/index.tsx
  82. 15 0
      packages/semi-ui/videoPlayer/utils.ts
  83. 193 0
      packages/semi-ui/videoPlayer/videoProgress.tsx
  84. 1 1
      packages/semi-webpack/package.json
  85. 216 235
      sitemap.xml
  86. 3 0
      src/images/docIcons/doc-videoplayer.svg
  87. 76 51
      yarn.lock

+ 1 - 0
.github/workflows/cypress.yml

@@ -78,6 +78,7 @@ jobs:
                   parallel: true
                   group: 'Cypress - Chrome'
                   spec: cypress/e2e/*
+                  env: coverage=false
               env:
                   CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
                   # Recommended: pass the GitHub token lets this action correctly

+ 543 - 1
content/basic/icon/index-en-US.md

@@ -9,6 +9,548 @@ brief: Semantic vector graphics.
 ---
 
 ## Icon List
+
+
+<div style={{display: "none"}}>
+@douyinfe/semi-icons icon list:
+
+IconAbsoluteStroked
+IconActivity
+IconAlarm
+IconAlertCircle
+IconAlertTriangle
+IconAlignBottom
+IconAlignCenter
+IconAlignCenterVertical
+IconAlignHCenterStroked
+IconAlignHLeftStroked
+IconAlignHRightStroked
+IconAlignJustify
+IconAlignLeft
+IconAlignRight
+IconAlignTop
+IconAlignVBotStroked
+IconAlignVBottomStroked
+IconAlignVCenterStroked
+IconAlignVTopStroked
+IconApartment
+IconAppCenter
+IconApps
+IconArchive
+IconArrowDown
+IconArrowDownLeft
+IconArrowDownRight
+IconArrowLeft
+IconArrowRight
+IconArrowUp
+IconArrowUpLeft
+IconArrowUpRight
+IconArticle
+IconAscend
+IconAt
+IconBackTop
+IconBackward
+IconBarChartHStroked
+IconBarChartVStroked
+IconBeaker
+IconBell
+IconBellStroked
+IconBold
+IconBolt
+IconBookH5Stroked
+IconBookOpenStroked
+IconBookStroked
+IconBookmark
+IconBookmarkAddStroked
+IconBookmarkDeleteStroked
+IconBottomCenterStroked
+IconBottomLeftStroked
+IconBottomRightStroked
+IconBox
+IconBrackets
+IconBranch
+IconBriefStroked
+IconBriefcase
+IconBulb
+IconButtonStroked
+IconBytedanceLogo
+IconCalendar
+IconCalendarClock
+IconCalendarStroked
+IconCamera
+IconCandlestickChartStroked
+IconCaretdown
+IconCaretup
+IconCarouselStroked
+IconCart
+IconCaseSensitive
+IconCenterLeftStroked
+IconCenterRightStroked
+IconChainStroked
+IconCheckChoiceStroked
+IconCheckCircleStroked
+IconCheckList
+IconCheckboxIndeterminate
+IconCheckboxTick
+IconChecklistStroked
+IconChevronDown
+IconChevronDownStroked
+IconChevronLeft
+IconChevronRight
+IconChevronRightStroked
+IconChevronUp
+IconChevronUpDown
+IconClear
+IconClock
+IconClose
+IconCloud
+IconCloudStroked
+IconCloudUploadStroked
+IconCode
+IconCodeStroked
+IconCoinMoneyStroked
+IconColorPalette
+IconColumnsStroked
+IconCommand
+IconComment
+IconCommentStroked
+IconComponent
+IconComponentPlaceholderStroked
+IconComponentStroked
+IconConfigStroked
+IconConnectionPoint1
+IconConnectionPoint2
+IconContrast
+IconCopy
+IconCopyAdd
+IconCopyStroked
+IconCornerRadiusStroked
+IconCreditCard
+IconCrop
+IconCrossCircleStroked
+IconCrossStroked
+IconCrown
+IconCustomerSupport
+IconCustomerSupportStroked
+IconCustomize
+IconDelete
+IconDeleteStroked
+IconDescend
+IconDescend2
+IconDesktop
+IconDisc
+IconDislikeThumb
+IconDivide
+IconDongchediLogo
+IconDoubleChevronLeft
+IconDoubleChevronRight
+IconDownCircleStroked
+IconDownload
+IconDownloadStroked
+IconDuration
+IconEdit
+IconEdit2Stroked
+IconEditStroked
+IconElementStroked
+IconEmoji
+IconExit
+IconExpand
+IconExport
+IconExternalOpen
+IconExternalOpenStroked
+IconEyeClosed
+IconEyeClosedSolid
+IconEyeOpened
+IconEyedropper
+IconFacebook
+IconFaceuLogo
+IconFastForward
+IconFastFoward
+IconFavoriteList
+IconFeishuLogo
+IconFemale
+IconFigma
+IconFile
+IconFillStroked
+IconFilledArrowDown
+IconFilledArrowUp
+IconFilpVertical
+IconFilter
+IconFingerLeftStroked
+IconFixedStroked
+IconFlag
+IconFlipHorizontal
+IconFlowChartStroked
+IconFolder
+IconFolderOpen
+IconFolderStroked
+IconFollowStroked
+IconFont
+IconFontColor
+IconForward
+IconForwardStroked
+IconFullScreenStroked
+IconGallery
+IconGift
+IconGiftStroked
+IconGit
+IconGithubLogo
+IconGitlabLogo
+IconGlobe
+IconGlobeStroke
+IconGridRectangle
+IconGridSquare
+IconGridStroked
+IconGridView
+IconGridView1
+IconH1
+IconH2
+IconH3
+IconH4
+IconH5
+IconH6
+IconH7
+IconH8
+IconH9
+IconHandle
+IconHash
+IconHeartStroked
+IconHelm
+IconHelpCircle
+IconHelpCircleStroked
+IconHistogram
+IconHistory
+IconHn
+IconHome
+IconHomeStroked
+IconHorn
+IconHourglass
+IconHourglassStroked
+IconIdCard
+IconIdentity
+IconImage
+IconImageStroked
+IconImport
+IconInbox
+IconIndenpentCornersStroked
+IconIndentLeft
+IconIndentRight
+IconIndependentCornersStroked
+IconInfoCircle
+IconInherit
+IconInheritStroked
+IconInnerSectionStroked
+IconInstagram
+IconInteractiveStroked
+IconInviteStroked
+IconIssueStroked
+IconItalic
+IconJianying
+IconKanban
+IconKey
+IconKeyStroked
+IconLanguage
+IconLayers
+IconLeftCircleStroked
+IconLightningStroked
+IconLikeHeart
+IconLikeThumb
+IconLineChartStroked
+IconLineHeight
+IconLink
+IconList
+IconListView
+IconLive
+IconLoading
+IconLock
+IconLockStroked
+IconLoopTextStroked
+IconMail
+IconMailStroked
+IconMailStroked1
+IconMale
+IconMapPin
+IconMapPinStroked
+IconMarginLeftStroked
+IconMarginStroked
+IconMark
+IconMaximize
+IconMember
+IconMenu
+IconMicrophone
+IconMicrophoneOff
+IconMinimize
+IconMinus
+IconMinusCircle
+IconMinusCircleStroked
+IconMinusStroked
+IconModalStroked
+IconMoneyExchangeStroked
+IconMonitorStroked
+IconMoon
+IconMore
+IconMoreStroked
+IconMusic
+IconMusicNoteStroked
+IconMute
+IconNineGridStroked
+IconNoteMoneyStroked
+IconOption
+IconOrderedList
+IconOrderedListStroked
+IconPaperclip
+IconPause
+IconPercentage
+IconPhone
+IconPhoneStroke
+IconPieChart2Stroked
+IconPieChartStroked
+IconPiechartH5Stroked
+IconPipixiaLogo
+IconPlay
+IconPlayCircle
+IconPlus
+IconPlusCircle
+IconPlusCircleStroked
+IconPlusStroked
+IconPriceTag
+IconPrint
+IconPrizeStroked
+IconPulse
+IconPuzzle
+IconQingyan
+IconQrCode
+IconQuit
+IconQuote
+IconRadio
+IconRankingCardStroked
+IconRealSizeStroked
+IconRedo
+IconRedoStroked
+IconRefresh
+IconRefresh2
+IconRegExp
+IconReply
+IconReplyStroked
+IconResso
+IconRestart
+IconRingChartStroked
+IconRotate
+IconRotationStroked
+IconRoute
+IconRowsStroked
+IconSafe
+IconSave
+IconSaveStroked
+IconScan
+IconScissors
+IconSearch
+IconSearchStroked
+IconSectionStroked
+IconSemiLogo
+IconSend
+IconSendMsgStroked
+IconSendStroked
+IconServer
+IconServerStroked
+IconSetting
+IconSettingStroked
+IconShareMoneyStroked
+IconShareStroked
+IconShield
+IconShieldStroked
+IconShift
+IconShoppingBag
+IconShrink
+IconShrinkScreenStroked
+IconSidebar
+IconSignal
+IconSimilarity
+IconSmallTriangleDown
+IconSmallTriangleLeft
+IconSmallTriangleRight
+IconSmallTriangleTop
+IconSmartphoneCheckStroked
+IconSmartphoneStroked
+IconSong
+IconSonicStroked
+IconSort
+IconSortStroked
+IconSourceControl
+IconSpin
+IconStackBarChartStroked
+IconStar
+IconStarStroked
+IconStop
+IconStopwatchStroked
+IconStoryStroked
+IconStrikeThrough
+IconSun
+IconSync
+IconTabArrowStroked
+IconTabsStroked
+IconTaskMoneyStroked
+IconTemplate
+IconTemplateStroked
+IconTerminal
+IconTestScoreStroked
+IconText
+IconTextRectangle
+IconTextStroked
+IconThumbUpStroked
+IconTick
+IconTickCircle
+IconTicketCodeExchangeStroked
+IconTicketCodeStroked
+IconTiktokLogo
+IconTop
+IconTopCenterStroked
+IconTopLeftStroked
+IconTopRightStroked
+IconTopbuzzLogo
+IconToutiaoLogo
+IconTransparentStroked
+IconTreeTriangleDown
+IconTreeTriangleRight
+IconTriangleArrow
+IconTriangleArrowVertical
+IconTriangleDown
+IconTriangleUp
+IconTrueFalseStroked
+IconTvCheckedStroked
+IconTwitter
+IconTypograph
+IconUnChainStroked
+IconUnderline
+IconUndo
+IconUnlink
+IconUnlock
+IconUnlockStroked
+IconUpload
+IconUploadError
+IconUser
+IconUserAdd
+IconUserCardPhone
+IconUserCardVideo
+IconUserCircle
+IconUserCircleStroked
+IconUserGroup
+IconUserListStroked
+IconUserSetting
+IconUserStroked
+IconVennChartStroked
+IconVerify
+IconVersionStroked
+IconVideo
+IconVideoDouyinStroked
+IconVideoListStroked
+IconVideoStroked
+IconVideoUrlStroked
+IconVigoLogo
+IconVolume1
+IconVolume2
+IconVolumnSilent
+IconVoteStroked
+IconVoteVideoStroked
+IconWeibo
+IconWholeWord
+IconWifi
+IconWindowAdaptionStroked
+IconWrench
+IconXiguaLogo
+IconYoutube
+
+
+
+@douyinfe/semi-icons-lab icon list:
+
+IconAccessibility
+IconAnchor
+IconAutocomplete
+IconAvatar
+IconBackTop
+IconBadge
+IconBadgeStar
+IconBanner
+IconBreadcrumb
+IconButton
+IconCalendar
+IconCard
+IconCarousel
+IconCascader
+IconChangelog
+IconChart
+IconChat
+IconCheckbox
+IconCodeHighlight
+IconCollapse
+IconCollapsible
+IconColorPlatte
+IconColorPlatteNew
+IconConfig
+IconDarkMode
+IconDatePicker
+IconDescriptions
+IconDivider
+IconDropdown
+IconEmpty
+IconFaq
+IconForm
+IconGettingStarted
+IconGrid
+IconHeart
+IconHighlight
+IconImage
+IconInput
+IconInputNumber
+IconIntro
+IconJsonViewer
+IconLayout
+IconList
+IconLocaleProvider
+IconLottie
+IconMarkdown
+IconModal
+IconNavigation
+IconNotification
+IconOverflow
+IconPagination
+IconPincode
+IconPopconfirm
+IconPopover
+IconProgress
+IconRadio
+IconRating
+IconScrollList
+IconSelect
+IconSideSheet
+IconSkeleton
+IconSlider
+IconSpace
+IconSpin
+IconSteps
+IconSwitch
+IconTable
+IconTabs
+IconTag
+IconTagInput
+IconTimePicker
+IconTimeline
+IconToast
+IconToken
+IconTooltip
+IconTransfer
+IconTree
+IconTreeSelect
+IconTypography
+IconUpload
+IconVersionOne
+IconVersionTwo
+IconWebcomponents
+IconWheelChair
+
+</div>
+
 ```icon
 ```
 
@@ -174,4 +716,4 @@ import { IconHome } from '@douyinfe/semi-icons';
 () => <IconHome aria-label="back to homepage" />;
 ```
 
-- The svg element inside Icon is a decorative element, and aria-hidden is set by default to prevent it from being read by screen readers
+- The svg element inside Icon is a decorative element, and aria-hidden is set by default to prevent it from being read by screen readers

+ 542 - 0
content/basic/icon/index.md

@@ -13,6 +13,548 @@ brief: 语义化的矢量图形。
 
 `@douyinfe/semi-icons-lab` 为彩色图标集,需单独安装,不可改色, lab 图标集于 v2.48 后提供
 
+<div style={{display: "none"}}>
+@douyinfe/semi-icons icon list:
+
+IconAbsoluteStroked
+IconActivity
+IconAlarm
+IconAlertCircle
+IconAlertTriangle
+IconAlignBottom
+IconAlignCenter
+IconAlignCenterVertical
+IconAlignHCenterStroked
+IconAlignHLeftStroked
+IconAlignHRightStroked
+IconAlignJustify
+IconAlignLeft
+IconAlignRight
+IconAlignTop
+IconAlignVBotStroked
+IconAlignVBottomStroked
+IconAlignVCenterStroked
+IconAlignVTopStroked
+IconApartment
+IconAppCenter
+IconApps
+IconArchive
+IconArrowDown
+IconArrowDownLeft
+IconArrowDownRight
+IconArrowLeft
+IconArrowRight
+IconArrowUp
+IconArrowUpLeft
+IconArrowUpRight
+IconArticle
+IconAscend
+IconAt
+IconBackTop
+IconBackward
+IconBarChartHStroked
+IconBarChartVStroked
+IconBeaker
+IconBell
+IconBellStroked
+IconBold
+IconBolt
+IconBookH5Stroked
+IconBookOpenStroked
+IconBookStroked
+IconBookmark
+IconBookmarkAddStroked
+IconBookmarkDeleteStroked
+IconBottomCenterStroked
+IconBottomLeftStroked
+IconBottomRightStroked
+IconBox
+IconBrackets
+IconBranch
+IconBriefStroked
+IconBriefcase
+IconBulb
+IconButtonStroked
+IconBytedanceLogo
+IconCalendar
+IconCalendarClock
+IconCalendarStroked
+IconCamera
+IconCandlestickChartStroked
+IconCaretdown
+IconCaretup
+IconCarouselStroked
+IconCart
+IconCaseSensitive
+IconCenterLeftStroked
+IconCenterRightStroked
+IconChainStroked
+IconCheckChoiceStroked
+IconCheckCircleStroked
+IconCheckList
+IconCheckboxIndeterminate
+IconCheckboxTick
+IconChecklistStroked
+IconChevronDown
+IconChevronDownStroked
+IconChevronLeft
+IconChevronRight
+IconChevronRightStroked
+IconChevronUp
+IconChevronUpDown
+IconClear
+IconClock
+IconClose
+IconCloud
+IconCloudStroked
+IconCloudUploadStroked
+IconCode
+IconCodeStroked
+IconCoinMoneyStroked
+IconColorPalette
+IconColumnsStroked
+IconCommand
+IconComment
+IconCommentStroked
+IconComponent
+IconComponentPlaceholderStroked
+IconComponentStroked
+IconConfigStroked
+IconConnectionPoint1
+IconConnectionPoint2
+IconContrast
+IconCopy
+IconCopyAdd
+IconCopyStroked
+IconCornerRadiusStroked
+IconCreditCard
+IconCrop
+IconCrossCircleStroked
+IconCrossStroked
+IconCrown
+IconCustomerSupport
+IconCustomerSupportStroked
+IconCustomize
+IconDelete
+IconDeleteStroked
+IconDescend
+IconDescend2
+IconDesktop
+IconDisc
+IconDislikeThumb
+IconDivide
+IconDongchediLogo
+IconDoubleChevronLeft
+IconDoubleChevronRight
+IconDownCircleStroked
+IconDownload
+IconDownloadStroked
+IconDuration
+IconEdit
+IconEdit2Stroked
+IconEditStroked
+IconElementStroked
+IconEmoji
+IconExit
+IconExpand
+IconExport
+IconExternalOpen
+IconExternalOpenStroked
+IconEyeClosed
+IconEyeClosedSolid
+IconEyeOpened
+IconEyedropper
+IconFacebook
+IconFaceuLogo
+IconFastForward
+IconFastFoward
+IconFavoriteList
+IconFeishuLogo
+IconFemale
+IconFigma
+IconFile
+IconFillStroked
+IconFilledArrowDown
+IconFilledArrowUp
+IconFilpVertical
+IconFilter
+IconFingerLeftStroked
+IconFixedStroked
+IconFlag
+IconFlipHorizontal
+IconFlowChartStroked
+IconFolder
+IconFolderOpen
+IconFolderStroked
+IconFollowStroked
+IconFont
+IconFontColor
+IconForward
+IconForwardStroked
+IconFullScreenStroked
+IconGallery
+IconGift
+IconGiftStroked
+IconGit
+IconGithubLogo
+IconGitlabLogo
+IconGlobe
+IconGlobeStroke
+IconGridRectangle
+IconGridSquare
+IconGridStroked
+IconGridView
+IconGridView1
+IconH1
+IconH2
+IconH3
+IconH4
+IconH5
+IconH6
+IconH7
+IconH8
+IconH9
+IconHandle
+IconHash
+IconHeartStroked
+IconHelm
+IconHelpCircle
+IconHelpCircleStroked
+IconHistogram
+IconHistory
+IconHn
+IconHome
+IconHomeStroked
+IconHorn
+IconHourglass
+IconHourglassStroked
+IconIdCard
+IconIdentity
+IconImage
+IconImageStroked
+IconImport
+IconInbox
+IconIndenpentCornersStroked
+IconIndentLeft
+IconIndentRight
+IconIndependentCornersStroked
+IconInfoCircle
+IconInherit
+IconInheritStroked
+IconInnerSectionStroked
+IconInstagram
+IconInteractiveStroked
+IconInviteStroked
+IconIssueStroked
+IconItalic
+IconJianying
+IconKanban
+IconKey
+IconKeyStroked
+IconLanguage
+IconLayers
+IconLeftCircleStroked
+IconLightningStroked
+IconLikeHeart
+IconLikeThumb
+IconLineChartStroked
+IconLineHeight
+IconLink
+IconList
+IconListView
+IconLive
+IconLoading
+IconLock
+IconLockStroked
+IconLoopTextStroked
+IconMail
+IconMailStroked
+IconMailStroked1
+IconMale
+IconMapPin
+IconMapPinStroked
+IconMarginLeftStroked
+IconMarginStroked
+IconMark
+IconMaximize
+IconMember
+IconMenu
+IconMicrophone
+IconMicrophoneOff
+IconMinimize
+IconMinus
+IconMinusCircle
+IconMinusCircleStroked
+IconMinusStroked
+IconModalStroked
+IconMoneyExchangeStroked
+IconMonitorStroked
+IconMoon
+IconMore
+IconMoreStroked
+IconMusic
+IconMusicNoteStroked
+IconMute
+IconNineGridStroked
+IconNoteMoneyStroked
+IconOption
+IconOrderedList
+IconOrderedListStroked
+IconPaperclip
+IconPause
+IconPercentage
+IconPhone
+IconPhoneStroke
+IconPieChart2Stroked
+IconPieChartStroked
+IconPiechartH5Stroked
+IconPipixiaLogo
+IconPlay
+IconPlayCircle
+IconPlus
+IconPlusCircle
+IconPlusCircleStroked
+IconPlusStroked
+IconPriceTag
+IconPrint
+IconPrizeStroked
+IconPulse
+IconPuzzle
+IconQingyan
+IconQrCode
+IconQuit
+IconQuote
+IconRadio
+IconRankingCardStroked
+IconRealSizeStroked
+IconRedo
+IconRedoStroked
+IconRefresh
+IconRefresh2
+IconRegExp
+IconReply
+IconReplyStroked
+IconResso
+IconRestart
+IconRingChartStroked
+IconRotate
+IconRotationStroked
+IconRoute
+IconRowsStroked
+IconSafe
+IconSave
+IconSaveStroked
+IconScan
+IconScissors
+IconSearch
+IconSearchStroked
+IconSectionStroked
+IconSemiLogo
+IconSend
+IconSendMsgStroked
+IconSendStroked
+IconServer
+IconServerStroked
+IconSetting
+IconSettingStroked
+IconShareMoneyStroked
+IconShareStroked
+IconShield
+IconShieldStroked
+IconShift
+IconShoppingBag
+IconShrink
+IconShrinkScreenStroked
+IconSidebar
+IconSignal
+IconSimilarity
+IconSmallTriangleDown
+IconSmallTriangleLeft
+IconSmallTriangleRight
+IconSmallTriangleTop
+IconSmartphoneCheckStroked
+IconSmartphoneStroked
+IconSong
+IconSonicStroked
+IconSort
+IconSortStroked
+IconSourceControl
+IconSpin
+IconStackBarChartStroked
+IconStar
+IconStarStroked
+IconStop
+IconStopwatchStroked
+IconStoryStroked
+IconStrikeThrough
+IconSun
+IconSync
+IconTabArrowStroked
+IconTabsStroked
+IconTaskMoneyStroked
+IconTemplate
+IconTemplateStroked
+IconTerminal
+IconTestScoreStroked
+IconText
+IconTextRectangle
+IconTextStroked
+IconThumbUpStroked
+IconTick
+IconTickCircle
+IconTicketCodeExchangeStroked
+IconTicketCodeStroked
+IconTiktokLogo
+IconTop
+IconTopCenterStroked
+IconTopLeftStroked
+IconTopRightStroked
+IconTopbuzzLogo
+IconToutiaoLogo
+IconTransparentStroked
+IconTreeTriangleDown
+IconTreeTriangleRight
+IconTriangleArrow
+IconTriangleArrowVertical
+IconTriangleDown
+IconTriangleUp
+IconTrueFalseStroked
+IconTvCheckedStroked
+IconTwitter
+IconTypograph
+IconUnChainStroked
+IconUnderline
+IconUndo
+IconUnlink
+IconUnlock
+IconUnlockStroked
+IconUpload
+IconUploadError
+IconUser
+IconUserAdd
+IconUserCardPhone
+IconUserCardVideo
+IconUserCircle
+IconUserCircleStroked
+IconUserGroup
+IconUserListStroked
+IconUserSetting
+IconUserStroked
+IconVennChartStroked
+IconVerify
+IconVersionStroked
+IconVideo
+IconVideoDouyinStroked
+IconVideoListStroked
+IconVideoStroked
+IconVideoUrlStroked
+IconVigoLogo
+IconVolume1
+IconVolume2
+IconVolumnSilent
+IconVoteStroked
+IconVoteVideoStroked
+IconWeibo
+IconWholeWord
+IconWifi
+IconWindowAdaptionStroked
+IconWrench
+IconXiguaLogo
+IconYoutube
+
+
+
+@douyinfe/semi-icons-lab icon list:
+
+IconAccessibility
+IconAnchor
+IconAutocomplete
+IconAvatar
+IconBackTop
+IconBadge
+IconBadgeStar
+IconBanner
+IconBreadcrumb
+IconButton
+IconCalendar
+IconCard
+IconCarousel
+IconCascader
+IconChangelog
+IconChart
+IconChat
+IconCheckbox
+IconCodeHighlight
+IconCollapse
+IconCollapsible
+IconColorPlatte
+IconColorPlatteNew
+IconConfig
+IconDarkMode
+IconDatePicker
+IconDescriptions
+IconDivider
+IconDropdown
+IconEmpty
+IconFaq
+IconForm
+IconGettingStarted
+IconGrid
+IconHeart
+IconHighlight
+IconImage
+IconInput
+IconInputNumber
+IconIntro
+IconJsonViewer
+IconLayout
+IconList
+IconLocaleProvider
+IconLottie
+IconMarkdown
+IconModal
+IconNavigation
+IconNotification
+IconOverflow
+IconPagination
+IconPincode
+IconPopconfirm
+IconPopover
+IconProgress
+IconRadio
+IconRating
+IconScrollList
+IconSelect
+IconSideSheet
+IconSkeleton
+IconSlider
+IconSpace
+IconSpin
+IconSteps
+IconSwitch
+IconTable
+IconTabs
+IconTag
+IconTagInput
+IconTimePicker
+IconTimeline
+IconToast
+IconToken
+IconTooltip
+IconTransfer
+IconTree
+IconTreeSelect
+IconTypography
+IconUpload
+IconVersionOne
+IconVersionTwo
+IconWebcomponents
+IconWheelChair
+
+</div>
+
+
+
 ```icon
 ```
 

+ 1 - 0
content/order.js

@@ -91,6 +91,7 @@ const order = [
     'locale',
     'jsonviewer',
     'audioPlayer',
+    'videoPlayer',
 ];
 let { exec } = require('child_process');
 let fs = require('fs');

+ 321 - 0
content/plus/videoPlayer/index-en-US.md

@@ -0,0 +1,321 @@
+---
+order: 93
+localeCode: en-US
+category: Plus
+title: VideoPlayer
+width: 60%
+icon: doc-videoplayer
+brief: Used to play video
+showNew: true
+---
+
+## Demos
+
+### How to import
+
+```jsx import
+import { VideoPlayer } from '@douyinfe/semi-ui';
+```
+
+### Basic Usage
+For basic usage, pass the video address through `src` and pass the video cover address through `poster`
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+        />
+    );
+};
+```
+
+### Set controls list
+Set the display items of the menu bar through `controlsList`. The accepted value is an array. The default value is `['play', 'next', 'time', 'volume', 'playbackRate', 'quality', 'route', 'mirror', 'fullscreen', 'pictureInPicture']`
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    const controlsList = ['play', 'time', 'volume', 'playbackRate', 'fullscreen'];
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+            controlsList={controlsList}
+        />
+    );
+};
+```
+
+### Loop playback
+Use `loop` to set loop playback
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer 
+            height={630}
+            src={src}
+            poster={poster}
+            loop={true}
+        />
+    );
+};
+```
+
+### Fast forward and rewind
+Use `seekTime` to set the fast forward and rewind time, and use the left and right keys on the keyboard to fast forward and rewind.
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer, Select } from '@douyinfe/semi-ui';
+
+() => {
+    const [seekTime, setSeekTime] = useState(5);
+
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <div>
+            <span style={{ marginBottom: 10 }}>Please select the fast forward and rewind time</span>
+            <Select
+                value={seekTime}
+                style={{ width: 100, marginLeft: 10 }}
+                onChange={(value) => setSeekTime(value)}
+                optionList={[
+                    { label: '5s', value: 5 },
+                    { label: '10s', value: 10 },
+                    { label: '15s', value: 15 },
+                ]}
+                placeholder='Please select the fast forward and rewind time'
+            />
+            <VideoPlayer 
+                height={630}
+                style={{ marginTop: 10 }}
+                src={src}
+                poster={poster}
+                seekTime={seekTime}
+            />
+        </div>
+    );
+};
+```
+
+### Playback rate
+Set the rate selection list through `playbackRateList`
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const playbackRateList = [
+        { label: '0.5x', value: 0.5 },
+        { label: '1.0x', value: 1 },
+        { label: '1.5x', value: 1.5 },
+        { label: '2.0x', value: 2 },
+    ];
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+            playbackRateList={playbackRateList}
+        />
+    );
+};
+```
+
+### Volume setting
+Use `volume` to set the initial volume, the value range is 0 - 100, set `muted` to `true` to play silently
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+            muted={true}
+        />
+    );
+};
+```
+
+### Clarity switching
+Use `qualityList` to set the clarity selection list, `defaultQuality` to set the initial selected clarity, and `onQualityChange` to set the `src` logic to be updated after clicking.
+
+Similarly for route switching, use `routeList` to set the clarity selection list, `defaultRoute` to set the initial selected route, and `onRouteChange` to set the `src` logic to be updated after clicking.
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const [src, setSrc] = useState('https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4');
+
+    const playList = [
+        {
+            src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4',
+            quality: '1080p',
+        },
+        {
+            src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/video/vchart-show-video-480p.mp4',
+            quality: '480p',
+        },
+    ];
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+
+    const updateVideoSource = (quality) => {
+        const source = playList.find((item) => item.quality === quality);
+        setSrc(source.src);
+    };
+
+    return (
+        <VideoPlayer 
+            height={630}
+            src={src}
+            poster={poster}
+            defaultQuality={'1080p'}
+            qualityList={[
+                { label: '1080p', value: '1080p' },
+                { label: '480p', value: '480p' },
+            ]}
+            onQualityChange={(quality) => {
+                console.log('quality change', quality);
+                updateVideoSource(quality);
+            }}
+        />
+    );
+};
+```
+
+### Chapter markers
+Set chapter markers via `markers`
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    const markers = [
+        {
+            start: 0,
+            title: 'Start'
+        },
+        {
+            start: 4,
+            title: 'Function Introduction'
+        },
+        {
+            start: 38,
+            title: 'Figma Plugin'
+        },
+        {
+            start: 51,
+            title: 'Ending'
+        }
+    ];
+
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+            markers={markers}
+        />
+    );
+};
+```
+
+### Theme
+Set the theme via `theme`, the theme only affects the background color
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer 
+            height={630}
+            src={src}
+            poster={poster}
+            theme={'light'}
+        />
+    );
+};
+```
+
+### API
+
+| Properties | Description | Type | Default Value |
+|------------|---------------------------------------------|--------------------------------------|-------|
+| autoPlay   | Whether to play automatically                                  | boolean                              | false   |
+| captionsSrc   | captions source                                | string                              | -   |
+| className  | Class name                                         | string                               | -   |
+| clickToPlay | Whether to enable click to play                         | boolean                              | true   |
+| controlsList | Set the menu bar to display controls. All controls are displayed by default.                       | string[]                              | ['play', 'next', 'time', 'volume', 'playbackRate', 'quality', 'route', 'mirror', 'fullscreen', 'pictureInPicture']   |
+| crossOrigin | This enum attribute indicates whether CORS is used to fetch the video. CORS-enabled resources can be reused in 'canvas' elements without being polluted. Allowed values ​​are 'anonymous' and 'use-credentials' | 'anonymous' \|'use-credentials'   | - |
+| defaultPlaybackRate | Default playback rate                            | number                              | 1   |
+| defaultPlaybackRate | Default video resolution                       | string                              | -   |
+| defaultRoute | Default Line                       | string                              | -   |
+| height | height                       | string \| number                                    | -   |
+| loop | Whether to enable loop playback                                   | boolean                              | false   |
+| markers | Chapter marking                                 | Marker[]                              | -   |
+| muted | Whether to play silently                                   | boolean                              | false   |
+| onPause | Pause callback                                       | () => void                            | -   |
+| onPlay | Play callback                                       | () => void                            | -   |
+| onQualityChange | Switch quality callback                                       | (quality: string) => void                            | -   |
+| onRateChange | Switch rate callback                                               | (rate: number) => void                            | -   |
+| onRouteChange | Switch route callback                                                | (route: string) => void                            | -   |
+| onVolumeChange | Adjust volume callback                                       | (volume: number) => void                            | -   |
+| playbackRateList | Rate list, 6 playback rates are displayed by default, namely 0.5, 0.75, 1.0, 1.25, 1.5 and 2.0                     | Array<{ label: string; value: string }>                              | -   |
+| poster | Poster                       | string                              | -   |
+| qualityList | Quality list                      | Array<{ label: string; value: string }>                              | -   |
+| routeList | Route list                       | Array<{ label: string; value: string }>                              | -   |
+| seekTime | Fast forward and rewind time                   | number                              | 10   |
+| src | Video playback address                       | string                              | -   |
+| style | Style                                          | CSSProperties                        | - |
+| theme | Theme settings, different theme components have different background colors | 'dark' \| 'light' | 'dark' |
+| volume | Default volume                      | number                              | 100  |
+| width | width                   | string \| number                                | -   |
+
+
+#### Marker
+| Properties | Description | Type | Default Value |
+|------------|---------------------------------------------|--------------------------------------|-------|
+| start   | start time point                                  | number                              | 
+| title   | title                                  | string                              | 
+
+
+## Design Token
+
+<DesignToken/>

+ 324 - 0
content/plus/videoPlayer/index.md

@@ -0,0 +1,324 @@
+---
+order: 93
+localeCode: zh-CN
+category: Plus
+title: VideoPlayer 视频播放器
+width: 60%
+icon: doc-videoplayer
+brief: 用于播放视频
+showNew: true
+---
+
+## 代码演示
+
+### 如何引入
+
+```jsx import
+import { VideoPlayer } from '@douyinfe/semi-ui';
+```
+
+### 基本用法
+基本使用,通过 `src` 传入视频地址, 通过 `poster` 传入视频封面地址
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+        />
+    );
+};
+```
+
+### 设置菜单栏功能
+通过 `controlsList` 设置菜单栏的展示项,该项接受值为数组,默认值为`['play', 'next', 'time', 'volume', 'playbackRate', 'quality', 'route', 'mirror', 'fullscreen', 'pictureInPicture']`
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    const controlsList = ['play', 'time', 'volume', 'playbackRate', 'fullscreen'];
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+            controlsList={controlsList}
+        />
+    );
+};
+```
+
+### 循环播放
+通过 `loop` 设置循环播放
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer 
+            height={630}
+            src={src}
+            poster={poster}
+            loop={true}
+        />
+    );
+};
+```
+
+### 快进快退
+通过 `seekTime` 设置快进快退时间,通过键盘左右键执行快进快退
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer, Select } from '@douyinfe/semi-ui';
+
+() => {
+    const [seekTime, setSeekTime] = useState(5);
+
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <div>
+            <div>
+                <span style={{ marginBottom: 10 }}>请选择快进快退时间</span>
+                <Select
+                    value={seekTime}
+                    style={{ width: 100, marginLeft: 10 }}
+                    onChange={(value) => setSeekTime(value)}
+                    optionList={[
+                        { label: '5s', value: 5 },
+                        { label: '10s', value: 10 },
+                        { label: '15s', value: 15 },
+                    ]}
+                    placeholder='请选择快进快退时间'
+                />
+            </div>
+            <VideoPlayer 
+                height={630}
+                style={{ marginTop: 10 }}
+                src={src}
+                poster={poster}
+                seekTime={seekTime}
+            />
+        </div>
+    );
+};
+```
+
+### 播放速率
+通过 `playbackRateList` 设置速率选择列表
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const playbackRateList = [
+        { label: '0.5x', value: 0.5 },
+        { label: '1.0x', value: 1 },
+        { label: '1.5x', value: 1.5 },
+        { label: '2.0x', value: 2 },
+    ];
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+            playbackRateList={playbackRateList}
+        />
+    );
+};
+```
+
+### 音量设置
+通过 `volume` 设置初始音量,值区间为 0 - 100, 设置 `muted` 为 `true` 可以静音播放
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+            muted={true}
+        />
+    );
+};
+```
+
+### 清晰度切换
+通过 `qualityList` 设置清晰度选择列表,`defaultQuality` 设置初始选择的清晰度,`onQualityChange` 设置点击后更新的 `src` 逻辑。
+
+线路切换同理,通过 `routeList` 设置清晰度选择列表,`defaultRoute` 设置初始选择的线路,`onRouteChange` 设置点击后更新的 `src` 逻辑。
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const [src, setSrc] = useState('https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4');
+
+    const playList = [
+        {
+            src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4',
+            quality: '1080p',
+        },
+        {
+            src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/video/vchart-show-video-480p.mp4',
+            quality: '480p',
+        },
+    ];
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+
+    const updateVideoSource = (quality) => {
+        const source = playList.find((item) => item.quality === quality);
+        setSrc(source.src);
+    };
+
+    return (
+        <VideoPlayer 
+            height={630}
+            src={src}
+            poster={poster}
+            defaultQuality={'1080p'}
+            qualityList={[
+                { label: '1080p', value: '1080p' },
+                { label: '480p', value: '480p' },
+            ]}
+            onQualityChange={(quality) => {
+                console.log('quality change', quality);
+                updateVideoSource(quality);
+            }}
+        />
+    );
+};
+```
+
+### 章节标记
+通过 `markers` 设置章节标记点
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    const markers = [
+        {
+            start: 0,
+            title: '片头'
+        },
+        {
+            start: 4,
+            title: '功能介绍'
+        },
+        {
+            start: 38,
+            title: 'Figma Plugin'
+        },
+        {
+            start: 51,
+            title: '片尾'
+        }
+    ];
+
+    return (
+        <VideoPlayer
+            height={630} 
+            src={src}
+            poster={poster}
+            markers={markers}
+        />
+    );
+};
+```
+
+### 主题
+通过 `theme` 设置主题, 主题仅影响背景色
+
+```jsx live=true dir="column"
+import React from 'react';
+import { VideoPlayer } from '@douyinfe/semi-ui';
+
+() => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer 
+            src={src}
+            poster={poster}
+            height={630}
+            theme={'light'}
+        />
+    );
+};
+```
+
+### API
+
+| 属性        | 说明                                        | 类型                                  | 默认值   |
+|------------|---------------------------------------------|--------------------------------------|-------|
+| autoPlay   | 是否自动播放                                  | boolean                              | false   |
+| captionsSrc   | 字幕资源                                  | string                              | -   |
+| className  | 类名                                         | string                               | -   |
+| clickToPlay | 是否启用点击以播放                            | boolean                              | true   |
+| controlsList | 设置菜单栏展示控件,默认展示所有控件                       | string[]                              | ['play', 'next', 'time', 'volume', 'playbackRate', 'quality', 'route', 'mirror', 'fullscreen', 'pictureInPicture']   |
+| crossOrigin | 该枚举属性指明是否使用 CORS 来获取相关视频。允许 CORS 的资源可在 'canvas' 元素中被重用,而不会被污染。允许的值有 'anonymous' 和 'use-credentials'  | 'anonymous' \|'use-credentials'                                | -  |
+| defaultPlaybackRate | 默认倍率                            | number                              | 1   |
+| defaultPlaybackRate | 默认视频清晰度                       | string                              | -   |
+| defaultRoute | 默认线路                       | string                              | -   |
+| height | 高度                       | string \| number                                    | -   |
+| loop | 是否启用循环播放                                   | boolean                              | false   |
+| markers | 节点标记                                 | Marker[]                              | -   |
+| muted | 是否静音播放                                   | boolean                              | false   |
+| onPause | 暂停回调                                       | () => void                            | -   |
+| onPlay | 播放回调                                       | () => void                            | -   |
+| onQualityChange | 切换清晰度回调                                       | (quality: string) => void                            | -   |
+| onRateChange | 切换速率回调                                       | (rate: number) => void                            | -   |
+| onRouteChange | 切换线路回调                                       | (route: string) => void                            | -   |
+| onVolumeChange | 调整音量回调                                       | (volume: number) => void                            | -   |
+| playbackRateList | 速率列表,默认展示 6 种播放速率,分别为 0.5,0.75,1.0,1.25,1.5 和 2.0                      | Array<{ label: string; value: string }>                              | -   |
+| poster | 封面图                       | string                              | -   |
+| qualityList | 清晰度列表                      | Array<{ label: string; value: string }>                              | -   |
+| routeList | 线路列表                       | Array<{ label: string; value: string }>                              | -   |
+| seekTime | 快进快退时间                   | number                              | 10   |
+| src | 视频播放地址                       | string                              | -   |
+| style | 样式                                          | CSSProperties                        | - |
+| theme | 主题设置,不同主题组件的背景色不同                 | 'dark' \| 'light'                        | 'dark' |
+| volume | 默认音量                      | number                              | 100  |
+| width | 宽度                   | string \| number                                | -   |
+
+
+#### Marker
+| 属性        | 说明                                        | 类型                                  | 默认值   |
+|------------|---------------------------------------------|--------------------------------------|-------|
+| start   | 起始时间点                                  | number                              | 
+| title   | 标题                                  | string                              | 
+
+
+## 设计变量
+
+<DesignToken/>

+ 1 - 1
content/show/cropper/index-en-US.md

@@ -23,7 +23,7 @@ import { Cropper } from '@douyinfe/semi-ui';
 
 ### Basic usage
 
-Use `sr` to set the cropped image; use `shape` to set the shape of the cropping box, which defaults to square.
+Use `src` to set the cropped image; use `shape` to set the shape of the cropping box, which defaults to square.
 
 ```jsx live=true dir=column noInline=true
 import { Cropper, Button, RadioGroup, Radio } from '@douyinfe/semi-ui';

+ 1 - 1
content/show/cropper/index.md

@@ -26,7 +26,7 @@ import { Cropper } from '@douyinfe/semi-ui';
 
 ### 基本用法
 
-通过 `sr` 设置被裁切的图片; 可通过 `shape` 设置裁切框形状,默认为方形。
+通过 `src` 设置被裁切的图片; 可通过 `shape` 设置裁切框形状,默认为方形。
 
 ```jsx live=true dir=column noInline=true
 import { Cropper, Button, RadioGroup, Radio } from '@douyinfe/semi-ui';

+ 1 - 0
content/show/table/index-en-US.md

@@ -5464,6 +5464,7 @@ type Filter = {
 | renderCell         | Custom rendering checkbox                                                                                 | ({ selected: boolean, record: RecordType, originNode: JSX.Element, inHeader: boolean, disabled: boolean, indeterminate: boolean, index?: number, selectRow?: (selected: boolean, e: Event) => void, selectAll?: (selected: boolean, e: Event) => void }) => ReactNode |        |      **2.52.0**      |
 | width | Custom list selection box width | string | number |  |
 | onChange | A callback in the event of a change in the selected item. The first parameter will save the row keys selected last time, even if you do paging control or update the dataSource [FAQ](#faq) | (selectedRowKeys: number[]\|string[], selectedRows: RecordType[]) => void |  |  |
+| onHeaderCell | Set the head cell property | (column: RecordType, columnIndex: number) => object |  |
 | onSelect | Callback when the user manually clicks the selection box of a row | (record: RecordType, selected: boolean, selectedRows: RecordType[], nativeEvent: MouseEvent) => void |  |  |
 | onSelectAll | The user manually clicks the callback of the header selection box, and all optional rows in the dataSource will be selected/unselected | (selected: boolean, selectedRows: RecordType[], changedRows: RecordType[]) => void |  |  |
 

+ 1 - 0
content/show/table/index.md

@@ -5859,6 +5859,7 @@ type Filter = {
 | shouldCellUpdate | 自定义控制单元格是否渲染。默认 cell 会深对比 props 和 nextProps 是否变化,来决定是否渲染单元格。如果你的 props 中的 record 比较复杂,建议使用 `shouldCellUpdate` 接管单元格的渲染。 | (props: TableCellProps, prevProps: TableCellProps) => boolean                                                                                                                                                                                                         |        | **2.71.0** |
 | width            | 自定义列表选择框宽度                                                                                                                                                           | string\|number                                                                                                                                                                                                                                                        |        |            |
 | onChange         | 选中项发生变化时的回调。第一个参数会保存上次选中的 row keys,即使你做了分页受控或更新了 dataSource [FAQ](#faq)                                                                   | (selectedRowKeys: number[]\|string[], selectedRows: RecordType[]) => void                                                                                                                                                                                             |        |            |
+| onHeaderCell | 设置头部单元格属性 | (column: RecordType, columnIndex: number) => object |  |
 | onSelect         | 用户手动点击某行选择框的回调                                                                                                                                                   | (record: RecordType, selected: boolean, selectedRows: RecordType[], nativeEvent: MouseEvent) => void                                                                                                                                                                  |        |            |
 | onSelectAll      | 用户手动点击表头选择框的回调,会选中/取消选中 dataSource 里的所有可选行                                                                                                         | (selected: boolean, selectedRows: RecordType[], changedRows: RecordType[]) => void                                                                                                                                                                                    |        |            |
 

+ 24 - 0
content/start/changelog/index-en-US.md

@@ -121,6 +121,30 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
     - Fixed the problem that  Form Label lost padding right(effect version v2.23.1) [#1258](https://github.com/DouyinFE/semi-design/pull/1258)
     - The Switch component Design Token is updated, adding `$spacing-switch_knob-left`; `$motion-switch_unchecked-translateX` is corrected to more semantically `$spacing-switch_unchecked-translateX` [#1267](https://github.com/DouyinFE/semi-design/pull/1267)
 
+#### 🎉 2.81.0 (2025-06-16)
+- 【Fix】
+    - Fixed the problem that when maxLength and getValueLength are set at the same time in the Input、TextArea  component, the Chinese input will be truncated before the input is completed  [#2858](https://github.com/DouyinFE/semi-design/issues/2858)
+- 【Style】
+    - For range-type DatePicker, increase the priority of the input background color set in the hover/active state [#2856](https://github.com/DouyinFE/semi-design/pull/2856)
+- 【Design Token】
+    - Add $color-button_disabled_outline_text-default to set the disabled state text color of Button in outline mode  [#2861](https://github.com/DouyinFE/semi-design/issues/2861) 
+
+
+#### 🎉 2.81.0-beta.0 (2025-06-10)
+- 【Style】
+    - Adjust the height of the input-wrapper layer in the range type DatePicker to center the content
+- 【Fix】
+    - Modify the timing of getting the container's position in Tooltip to improve the performance of component initialization
+
+
+#### 🎉 2.80.0 (2025-05-19)
+- 【Fix】
+    - Fixed the display problem of cascader with single selection, controlled value and value undefined, asynchronous loading, and showNext set to hover when loading multiple projects at the same time.  [#2831](https://github.com/DouyinFE/semi-design/issues/2831)  [#2832](https://github.com/DouyinFE/semi-design/pull/2832)
+
+#### 🎉 2.80.0-beta.0 (2025-05-14)
+- 【Feat】
+    - add VideoPlayer component support
+
 #### 🎉 2.79.0 (2025-05-08)
 - 【Feat】
     - Upload Added a pop-up file name prompt function when the file name is too long [@yatbfm](https://github.com/yatbfm)

+ 24 - 0
content/start/changelog/index.md

@@ -13,6 +13,30 @@ Semi 版本号遵循 **Semver** 规范(主版本号 - 次版本号 - 修订版
 -   修订版本号(patch):仅会进行 bugfix,发布时间不限
 -   不同版本间的详细关系,可查阅 [FAQ](/zh-CN/start/faq)
 
+#### 🎉 2.81.0 (2025-06-16)
+- 【Fix】
+    - 修复 Input、TextArea 组件在同时设置 maxLength 和getValueLength时候,中文输入会在未输入完成时候被截断 [#2858](https://github.com/DouyinFE/semi-design/issues/2858) [#2859](https://github.com/DouyinFE/semi-design/pull/2859)
+- 【Style】
+    - 对于范围类型的 DatePicker,增加其中设置的 hover/active 状态下的 input 背景色的优先级 [#2856](https://github.com/DouyinFE/semi-design/pull/2856)
+- 【Design Token】
+    - 增加 $color-button_disabled_outline_text-default 用于设置边框模式的 Button 的禁用状态文字颜色  [#2861](https://github.com/DouyinFE/semi-design/issues/2861) [#2857](https://github.com/DouyinFE/semi-design/pull/2857)
+
+
+#### 🎉 2.81.0-beta.0 (2025-06-10)
+- 【Style】
+    - 调整 range 类型的DatePicker 中 input-wrapper 层的高度, 让内容居中 [#2855](https://github.com/DouyinFE/semi-design/pull/2855)
+- 【Fix】
+    - 修复 Select 在分组 label 为 ReactNode 的情况下,filter 后 optionList 展示有误问题 [#2854](https://github.com/DouyinFE/semi-design/pull/2854)
+    - 修改 Tooltip 中获取 container 的 position 时机,提升组件初始化的性能 [#2841](https://github.com/DouyinFE/semi-design/pull/2841)
+
+#### 🎉 2.80.0 (2025-05-19)
+- 【Fix】
+    - 修复单选,受控 value且 value 为undefined,异步加载,showNext 为 hover 的 cascader 在同时加载多个项目时的显示问题  [#2831](https://github.com/DouyinFE/semi-design/issues/2831) [#2832](https://github.com/DouyinFE/semi-design/pull/2832)
+
+#### 🎉 2.80.0-beta.0 (2025-05-14)
+- 【Feat】
+    - 新增 VideoPlayer 组件 [#2822](https://github.com/DouyinFE/semi-design/pull/2822)
+
 #### 🎉 2.79.0 (2025-05-08)
 - 【Feat】
     - Upload 添加文件名超长时弹出文件名提示功能 [@yatbfm](https://github.com/yatbfm) [#2753](https://github.com/DouyinFE/semi-design/pull/2753)

+ 2 - 1
content/start/overview/index.md

@@ -28,7 +28,8 @@ Chat 对话,
 HotKeys 快捷键,
 DragMove 拖拽移动,
 JsonViewer Json编辑器,
-AudioPlayer 音频播放器
+AudioPlayer 音频播放器,
+VideoPlayer 视频播放器
 ```
 
 ## 输入类

+ 58 - 0
cypress/e2e/videoPlayer.spec.js

@@ -0,0 +1,58 @@
+describe('videoPlayer', () => {
+
+    it('basic element check', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=videoplayer--basic');
+        cy.get('.semi-videoPlayer-poster').should('exist');
+        cy.get('.semi-videoPlayer-wrapper-dark').should('exist');
+        cy.get('.semi-videoPlayer-error').should('not.exist');
+        cy.get('.semi-videoPlayer-pause').should('exist');
+        cy.get('img').should('have.attr', 'src', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg');
+        cy.get('video').should('have.attr', 'src', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4');
+    });
+    
+    it('control list', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=videoplayer--control-list');
+        cy.get('.semi-icon-flip-horizontal').should('not.exist');
+        cy.get('.semi-icon-mini_player').should('not.exist');
+    });
+
+    it('theme', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=videoplayer--theme');
+        cy.get('.semi-videoPlayer-wrapper-dark').should('exist');
+        cy.get('.semi-videoPlayer-wrapper-light').should('exist');
+    });
+
+    it('set seek time', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=videoplayer--set-seek-time');
+        cy.wait(500);
+        cy.get('body').type('{rightArrow}');
+        cy.get('.semi-videoPlayer-controls-time').contains('00:05').should('exist');
+    });
+
+    it('set play list', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=videoplayer--playback-rate-list');
+        cy.get('.semi-videoPlayer-controls-menu-item').contains('1.0x').trigger('mouseover');
+        cy.get('.semi-videoPlayer-controls-popup-menu-item').contains('1.0x').should('exist');
+        cy.get('.semi-videoPlayer-controls-popup-menu-item').contains('1.5x').should('exist');
+        cy.get('.semi-videoPlayer-controls-popup-menu-item').contains('2.0x').should('exist');
+        cy.get('.semi-videoPlayer-controls-popup-menu-item').contains('1.25x').should('not.exist');
+        cy.get('.semi-videoPlayer-controls-popup-menu-item').contains('0.75x').should('not.exist');
+    });
+
+    it('volume', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=videoplayer--volume');
+        cy.get('.semi-icon-mute').should('exist');
+    });
+
+    it('video load error', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=videoplayer--no-resource');
+        cy.get('.semi-videoPlayer').contains('视频加载错误').should('exist');
+    });
+
+    it('chapter', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=videoplayer--chapter');
+        cy.get('.semi-videoPlayer-progress-slider').should('have.length', 4);
+        cy.get('.semi-videoPlayer-progress-slider').eq(1).trigger('mouseover');
+        cy.get('.semi-tooltip-content').contains('功能介绍').should('exist');
+    });
+});

+ 1 - 1
lerna.json

@@ -1,5 +1,5 @@
 {
     "useWorkspaces": true,
     "npmClient": "yarn",
-    "version": "2.79.0"
+    "version": "2.81.0"
 }

+ 3 - 3
packages/semi-animation-react/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation-react",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "motion library for semi-ui-react",
     "keywords": [
         "motion",
@@ -25,8 +25,8 @@
         "prepublishOnly": "npm run build:lib"
     },
     "dependencies": {
-        "@douyinfe/semi-animation": "2.79.0",
-        "@douyinfe/semi-animation-styled": "2.79.0",
+        "@douyinfe/semi-animation": "2.81.0",
+        "@douyinfe/semi-animation-styled": "2.81.0",
         "classnames": "^2.2.6"
     },
     "devDependencies": {

+ 1 - 1
packages/semi-animation-styled/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation-styled",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "semi styled animation",
     "keywords": [
         "semi",

+ 1 - 1
packages/semi-animation/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "animation base library for semi-ui",
     "keywords": [
         "animation",

+ 1 - 1
packages/semi-eslint-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "eslint-plugin-semi-design",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "semi ui eslint plugin",
     "keywords": [
         "semi",

+ 5 - 1
packages/semi-foundation/button/button.scss

@@ -251,7 +251,7 @@ $module: #{$prefix}-button;
         color: $color-button_disabled_solid-text-default;
         cursor: not-allowed;
 
-        &:not(.#{$module}-borderless):not(.#{$module}-light):hover {
+        &:not(.#{$module}-borderless):not(.#{$module}-light):not(.#{$module}-outline):hover {
             color: $color-button_disabled-text-hover;
         }
 
@@ -259,6 +259,10 @@ $module: #{$prefix}-button;
         &.#{$module}-borderless {
             color: $color-button_disabled-text-default;
         }
+
+        &.#{$module}-outline {
+            color: $color-button_disabled_outline_text-default;
+        }
     }
 
     &-borderless {

+ 4 - 3
packages/semi-foundation/button/variables.scss

@@ -80,10 +80,11 @@ $color-button_tertiary_outline-border-default: var(--semi-color-border); // 边
 $color-button_tertiary_solid-text-default: var(--semi-color-text-1); // 浅色第三按钮文字颜色
 
 // disabled
-$color-button_disabled_solid-text-default: var(--semi-color-disabled-text); // 禁用按钮文字颜色
-$color-button_disabled-text-default: var(--semi-color-disabled-text); // 禁用按钮文字颜色
+$color-button_disabled_solid-text-default: var(--semi-color-disabled-text); // 禁用按钮文字颜色 - 深色主题
+$color-button_disabled-text-default: var(--semi-color-disabled-text); // 禁用按钮文字颜色 - 浅色主题或无背景
+$color-button_disabled_outline_text-default: var(--semi-color-disabled-text); // 禁用按钮文字颜色 - 边框模式
 $color-button_disabled-bg-default: var(--semi-color-disabled-bg); // 禁用按钮背景颜色
-$color-button_disabled-text-hover: $color-button_disabled-text-default; // 禁用按钮文字颜色 - 悬浮
+$color-button_disabled-text-hover: $color-button_disabled-text-default; // 禁用按钮文字颜色 - 深色主题 - 悬浮
 $color-button_disabled-bg-hover: var(--semi-color-disabled-bg); // 禁用按钮背景颜色 - 悬浮
 $color-button_disabled_primary-bg-default: $color-button_disabled-bg-default; // 禁用 primary 按钮背景颜色
 $color-button_disabled_secondary-bg-default: $color-button_disabled-bg-default; // 禁用 secondary 按钮背景颜色

+ 2 - 2
packages/semi-foundation/cascader/foundation.ts

@@ -242,7 +242,7 @@ export interface CascaderAdapter extends DefaultAdapter<BasicCascaderProps, Basi
     updateLoadedKeyRefValue: (keys: Set<string>) => void;
     getLoadedKeyRefValue: () => Set<string>;
     setEmptyContentMinWidth: (minWidth: number) => void;
-    getTriggerWidth: () => number;
+    getTriggerWidth: () => number
 }
 
 export default class CascaderFoundation extends BaseFoundation<CascaderAdapter, BasicCascaderProps, BasicCascaderInnerData> {
@@ -521,7 +521,7 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
             }
             keyEntities[key] = optionNotExist as BasicEntity;
             // Fix: 1155, if the data is loaded asynchronously to update treeData, the emptying operation should not be done when entering the updateSelectedKey method
-        } else if (loading) {
+        } else if (loading || loadingKeys?.size) {
             // Use assign to avoid overwriting the'not-exist- * 'property of keyEntities after asynchronous loading
             // Overwriting'not-exist- * 'will cause selectionContent to be emptied unexpectedly when clicking on a dropDown item
             updateStates.keyEntities = assign(keyEntityState, keyEntities);

+ 9 - 0
packages/semi-foundation/datePicker/datePicker.scss

@@ -934,7 +934,16 @@ $module-list: #{$prefix}-scrolllist;
 
                 .#{$prefix}-input-wrapper {
                     background-color: transparent;
+                    height: fit-content;
                     border: none;
+
+                    &:active  {
+                        background-color: transparent;
+                    }
+
+                    &:hover {
+                        background-color: transparent;
+                    }
                 }
 
                 &-focus {

+ 43 - 8
packages/semi-foundation/input/foundation.ts

@@ -36,6 +36,7 @@ class InputFoundation extends BaseFoundation<InputAdapter> {
     }
 
     _timer: number | null;
+    compositionEnter: boolean = false;
 
     constructor(adapter: InputAdapter) {
         super({ ...InputFoundation.inputDefaultAdapter, ...adapter });
@@ -55,28 +56,62 @@ class InputFoundation extends BaseFoundation<InputAdapter> {
     }
 
     handleChange(value: any, e: any) {
-        const { maxLength, minLength, getValueLength } = this._adapter.getProps();
         let nextValue = value;
-        if (maxLength && isFunction(getValueLength)) {
-            nextValue = this.handleVisibleMaxLength(value);
+        if (!this.compositionEnter) {
+            nextValue = this.getNextValue(nextValue);
+        }
+        this.changeInput(nextValue, e);
+    }
+
+    getNextValue = (value: any) => {
+        const { maxLength, minLength, getValueLength } = this._adapter.getProps();
+        if (!isFunction(getValueLength)) {
+            return value;
+        }
+        if (maxLength) {
+            return this.handleVisibleMaxLength(value);
         }
-        if (minLength && isFunction(getValueLength)) {
-            this.handleVisibleMinLength(nextValue);
+        if (minLength) {
+            this.handleVisibleMinLength(value);
         }
+        return value;
+    }
+
+    changeInput = (value: any, e: any) => {
         if (this._isControlledComponent()) {
             /**
              * If it is a controlled component, directly notify the caller of the modified value.
              * Truncate the input value from the input box if the input value exceeds the maximum length limit.
              *  Even in controlled components, characters that exceed the length limit cannot be entered through the input box.
              */
-            this._adapter.notifyChange(nextValue, e);
+            this._adapter.notifyChange(value, e);
         } else {
-            this._adapter.setValue(nextValue);
-            this._adapter.notifyChange(nextValue, e);
+            this._adapter.setValue(value);
+            this._adapter.notifyChange(value, e);
             // this.checkAllowClear(value);
         }
     }
 
+    handleCompositionStart = () => {
+        this.compositionEnter = true;
+    }
+
+    handleCompositionEnd = (e: any) => {
+        const value = e.target.value;
+        this.compositionEnter = false;
+        const { getValueLength, maxLength, minLength } = this.getProps();
+        if (!isFunction(getValueLength)) {
+            return;
+        }
+        if (maxLength) {
+            const nextValue = this.handleVisibleMaxLength(value);
+            nextValue !== value && this.changeInput(nextValue, e);
+        }
+        if (minLength) {
+            this.handleVisibleMinLength(value);
+        } 
+    }
+
     /**
      * Modify minLength to trigger browser check for minimum length
      * Controlled mode is not checked

+ 45 - 8
packages/semi-foundation/input/textareaFoundation.ts

@@ -42,6 +42,8 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
         };
     }
 
+    compositionEnter: boolean = false;
+
     constructor(adapter: TextAreaAdapter) {
         super({
             ...TextAreaFoundation.textAreaDefaultAdapter,
@@ -58,18 +60,53 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
     handleChange(value: string, e: any) {
         const { maxLength, minLength, getValueLength } = this._adapter.getProps();
         let nextValue = value;
-        if (maxLength && isFunction(getValueLength)) {
-            nextValue = this.handleVisibleMaxLength(value);
-        }
-        if (minLength && isFunction(getValueLength)) {
-            this.handleVisibleMinLength(nextValue);
+        if (!this.compositionEnter) {
+            nextValue = this.getNextValue(nextValue);
         }
+        this._changeValue(nextValue, e);
+    }
+
+    _changeValue = (value: any, e: any) => {
         if (this._isControlledComponent()) {
-            this._adapter.notifyChange(nextValue, e);
+            this._adapter.notifyChange(value, e);
         } else {
-            this._adapter.setValue(nextValue);
-            this._adapter.notifyChange(nextValue, e);
+            this._adapter.setValue(value);
+            this._adapter.notifyChange(value, e);
+        }
+    }
+
+    getNextValue = (value: any) => {
+        const { maxLength, minLength, getValueLength } = this._adapter.getProps();
+        if (!isFunction(getValueLength)) {
+            return value;
+        }
+        if (maxLength) {
+            return this.handleVisibleMaxLength(value);
+        }
+        if (minLength) {
+            this.handleVisibleMinLength(value);
+        }
+        return value;
+    }
+
+    handleCompositionStart = () => {
+        this.compositionEnter = true;
+    }
+
+    handleCompositionEnd = (e: any) => {
+        this.compositionEnter = false;
+        const { getValueLength, maxLength, minLength } = this.getProps();
+        if (!isFunction(getValueLength)) {
+            return;
+        }
+        const value = e.target.value;
+        if (maxLength) {
+            const nextValue = this.handleVisibleMaxLength(value);
+            nextValue !== value && this._changeValue(nextValue, e);
         }
+        if (minLength) {
+            this.handleVisibleMinLength(value);
+        } 
     }
 
     /**

+ 3 - 3
packages/semi-foundation/package.json

@@ -1,14 +1,14 @@
 {
     "name": "@douyinfe/semi-foundation",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "",
     "scripts": {
         "build:lib": "node ./scripts/compileLib.js",
         "prepublishOnly": "npm run build:lib"
     },
     "dependencies": {
-        "@douyinfe/semi-animation": "2.79.0",
-        "@douyinfe/semi-json-viewer-core": "2.79.0",
+        "@douyinfe/semi-animation": "2.81.0",
+        "@douyinfe/semi-json-viewer-core": "2.81.0",
         "@mdx-js/mdx": "^3.0.1",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",

+ 4 - 1
packages/semi-foundation/tooltip/foundation.ts

@@ -92,7 +92,6 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
         this._mounted = true;
         this._bindEvent();
         this._shouldShow();
-        this._initContainerPosition();
         if (!wrapperId) {
             this._adapter.setId();
         }
@@ -316,6 +315,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
     };
 
     show = () => {
+        this._initContainerPosition();
         if (this._adapter.getAnimatingState()) {
             return;
         }
@@ -1134,6 +1134,9 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
     }
 
     _initContainerPosition() {
+        if (this._adapter.getContainerPosition() || !this._adapter.containerIsBody()) {
+            return;
+        }
         this._adapter.updateContainerPosition();
     }
 

+ 7 - 0
packages/semi-foundation/videoPlayer/animation.scss

@@ -0,0 +1,7 @@
+$animation_duration-videoPlayer_controls-show: 500ms; // videoPlayer菜单栏-弹出动画-动画持续时间
+$animation_duration-videoPlayer_slider-in: 300ms; // videoPlayer进度条-弹出动画-动画持续时间
+$animation_duration-videoPlayer_slider-out: 300ms; // videoPlayer进度条-收起动画-动画持续时间
+
+
+$animation_function-videoPlayer_slider-in: cubic-bezier(0.62, 0.05, 0.36, 0.95); // videoPlayer进度条-弹出动画-动画插值函数
+$animation_function-videoPlayer_slider-out: cubic-bezier(0.62, 0.05, 0.36, 0.95); // videoPlayer进度条-收起动画-动画插值函数

+ 39 - 0
packages/semi-foundation/videoPlayer/constants.ts

@@ -0,0 +1,39 @@
+import { BASE_CLASS_PREFIX } from '../base/constants';
+
+const cssClasses = {
+    PREFIX: `${BASE_CLASS_PREFIX}-videoPlayer`,
+    PREFIX_CONTROLS: `${BASE_CLASS_PREFIX}-videoPlayer-controls`,
+    PREFIX_PROGRESS: `${BASE_CLASS_PREFIX}-videoPlayer-progress`,
+} as const;
+
+const strings = {
+    DARK: 'dark',
+    LIGHT: 'light',
+    PLAY: 'play',
+    NEXT: 'next',
+    TIME: 'time',
+    VOLUME: 'volume',
+    PLAYBACK_RATE: 'playbackRate',
+    QUALITY: 'quality',
+    ROUTE: 'route',
+    MIRROR: 'mirror',
+    FULLSCREEN: 'fullscreen',
+    PICTURE_IN_PICTURE: 'pictureInPicture',
+} as const;
+
+const numbers = {
+    DEFAULT_VOLUME: 100,
+    DEFAULT_SEEK_TIME: 10,
+    DEFAULT_VOLUME_STEP: 10,
+    DEFAULT_PLAYBACK_RATE: 1,
+} as const;
+
+const DEFAULT_PLAYBACK_RATE = [
+    { label: '2.0x', value: 2 },
+    { label: '1.5x', value: 1.5 },
+    { label: '1.25x', value: 1.25 },
+    { label: '1.0x', value: 1 },
+    { label: '0.75x', value: 0.75 },
+];
+
+export { cssClasses, strings, numbers, DEFAULT_PLAYBACK_RATE }; 

+ 332 - 0
packages/semi-foundation/videoPlayer/foundation.ts

@@ -0,0 +1,332 @@
+import BaseFoundation, { DefaultAdapter } from '../base/foundation';
+import { numbers } from './constants';
+import { throttle } from 'lodash';
+
+export interface VideoPlayerAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    getVideo: () => HTMLVideoElement | null;
+    getVideoWrapper: () => HTMLDivElement | null;
+    notifyPause: () => void;
+    notifyPlay: () => void;
+    notifyQualityChange: (quality: string) => void;
+    notifyRateChange: (rate: number) => void;
+    notifyRouteChange: (route: string) => void;
+    notifyVolumeChange: (volume: number) => void;
+    setBufferedValue: (bufferedValue: number) => void;
+    setCurrentTime: (currentTime: number) => void;
+    setIsError: (isError: boolean) => void;
+    setIsMirror: (isMirror: boolean) => void;
+    setIsPlaying: (isPlaying: boolean) => void;
+    setMuted: (muted: boolean) => void;
+    setNotificationContent: (content: string) => void;
+    setPlaybackRate: (rate: number) => void;
+    setQuality: (quality: string) => void;
+    setRoute: (route: string) => void;
+    setShowControls: (showControls: boolean) => void;
+    setShowNotification: (showNotification: boolean) => void;
+    setTotalTime: (totalTime: number) => void;
+    setVolume: (volume: number) => void
+}
+
+export default class VideoPlayerFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<VideoPlayerAdapter<P, S>, P, S> {
+    constructor(adapter: VideoPlayerAdapter<P, S>) {
+        super({ ...adapter });
+    }
+
+    private controlsTimer: NodeJS.Timeout | null;
+    private scrollPosition: { x: number; y: number } | null = null;
+
+    init() {
+        const { volume, muted } = this.getProps();
+        const video = this._adapter.getVideo();
+        if (video) {
+            this._adapter.setTotalTime(video.duration);
+            this.handleVolumeChange(muted ? 0 : volume);
+        }
+        this.registerEvent();
+    }
+
+    destroy() {
+        this.unregisterEvent();
+        this.clearTimer();
+    }
+
+    shouldShowControlItem(name: string) {
+        const { controlsList } = this.getProps();
+        if (controlsList.includes(name)) {
+            return true;
+        }
+        return false;
+    }
+
+    clearTimer() {
+        if (this.controlsTimer) {
+            clearTimeout(this.controlsTimer);
+        }
+    }
+
+    handleMouseMove = throttle(() => {
+        this._adapter.setShowControls(true);
+        this.clearTimer();
+        this.controlsTimer = setTimeout(() => {
+            this._adapter.setShowControls(false);
+        }, 3000);
+    }, 200);
+
+
+    handleTimeChange(value: number) {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        if (!Number.isNaN(value)) {
+            video.currentTime = value;
+            this._adapter.setCurrentTime(value);
+        }
+    }
+
+    handleTimeUpdate() {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        this._adapter.setCurrentTime(video.currentTime);
+    }
+
+    handleDurationChange() {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        this._adapter.setTotalTime(video.duration);
+    }
+
+    handleError() {
+        this._adapter.setIsError(true);
+    }
+
+    handlePlayOrPause() {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        video.paused ? this.handlePlay() : this.handlePause();
+    }
+
+    handlePlay() {
+        const video = this._adapter.getVideo();
+        if (video) {
+            video.play();
+            this._adapter.setIsPlaying(true);
+            this._adapter.notifyPlay();
+        }
+    }
+
+    handlePause() {
+        const video = this._adapter.getVideo();
+        if (video) {
+            video.pause();
+            this._adapter.setIsPlaying(false);
+            this._adapter.notifyPause();
+        }
+    }
+
+    handleCanPlay = () => {
+        this._adapter.setShowNotification(false);
+    }
+
+    handleWaiting = (locale: any) => {
+        this._adapter.setNotificationContent(locale.loading);
+        this._adapter.setShowNotification(true);
+    }
+
+    handleStalled = (locale: any) => {
+        this._adapter.setNotificationContent(locale.stall);
+        this._adapter.setShowNotification(true);
+    }
+
+    handleProgress = () => {
+        const video = this._adapter.getVideo();
+        if (video && video.buffered.length > 0) {
+            const bufferedEnd = video.buffered.end(video.buffered.length - 1);
+            this._adapter.setBufferedValue(bufferedEnd);
+        }
+    }
+
+    handleEnded = () => {
+        this._adapter.setIsPlaying(false);
+        this._adapter.setShowControls(true);
+    }
+
+    handleVolumeChange(value: number) {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        const volume = Math.floor(value > 0 ? value : 0);
+        video.volume = volume / 100;
+        this._adapter.setVolume(volume);
+        this._adapter.setMuted(volume === 0 ? true : false);
+    }
+
+    handleVolumeSilent = () => {
+        const video = this._adapter.getVideo();
+        const { volume, muted } = this.getStates();
+        if (!video) return;
+        if (muted) {
+            video.volume = volume / 100;
+            this._adapter.setVolume(volume);
+            this._adapter.setMuted(false);
+        } else {
+            video.volume = 0;
+            this._adapter.setMuted(true);
+        }
+    }
+
+    checkFullScreen() {
+        const videoWrapper = this._adapter.getVideoWrapper();
+        if (!videoWrapper) return false;
+        return !!(
+            document.fullscreenElement === videoWrapper ||
+            // @ts-ignore
+            document?.webkitFullscreenElement === videoWrapper ||
+            // @ts-ignore
+            document?.mozFullScreenElement === videoWrapper ||
+            // @ts-ignore
+            document?.msFullscreenElement === videoWrapper ||
+            // @ts-ignore
+            videoWrapper?.webkitDisplayingFullscreen  // iOS Safari 特殊处理
+        );
+    }
+
+    handleFullscreen = () => {
+        const videoWrapper = this._adapter.getVideoWrapper();
+        const isFullScreen = this.checkFullScreen();
+        if (videoWrapper) {
+            if (isFullScreen) {
+                document.exitFullscreen();
+            } else {
+                // record scroll position before entering fullscreen
+                this.scrollPosition = {
+                    x: window.scrollX,
+                    y: window.scrollY
+                };
+                videoWrapper.requestFullscreen();
+            }
+        }
+    }
+
+    handleRateChange(rate: { label: string; value: number }, locale: any) {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        video.playbackRate = rate.value;
+        this._adapter.setPlaybackRate(rate.value);
+        this._adapter.notifyRateChange(rate.value);
+        this.handleTemporaryNotification(locale.rateChange.replace('${rate}', rate.label));
+    }
+
+    handleQualityChange(quality: { label: string; value: string }, locale: any) {
+        this._adapter.setQuality(quality.value);
+        this._adapter.notifyQualityChange(quality.value);
+        this.handleTemporaryNotification(locale.qualityChange.replace('${quality}', quality.label));
+        this.restorePlayPosition();
+    }
+
+    handleRouteChange(route: { label: string; value: string }, locale: any) {
+        this._adapter.setRoute(route.value);
+        this._adapter.notifyRouteChange?.(route.value);
+        this.handleTemporaryNotification(locale.routeChange.replace('${route}', route.label));
+        this.restorePlayPosition();
+    }
+
+    handleMirror = (locale: any) => {
+        const { isMirror } = this.getStates();
+        this._adapter.setIsMirror(!isMirror);
+        this.handleTemporaryNotification(!isMirror ? locale.mirror : locale.cancelMirror);
+    }
+
+    handlePictureInPicture = () => {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        video.requestPictureInPicture();
+    }
+
+    handleLeavePictureInPicture = () => {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        this._adapter.setIsPlaying(!video.paused);
+    };
+
+    handleTemporaryNotification = (content: string) => {
+        this._adapter.setNotificationContent(content);
+        this._adapter.setShowNotification(true);
+        setTimeout(() => {
+            this._adapter.setShowNotification(false);
+        }, 1000);
+    }
+
+    restorePlayPosition() {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        const wasPlaying = !video.paused;
+        const currentTime = video.currentTime;
+
+        const handleLoaded = () => {
+            video.currentTime = currentTime;
+            if (wasPlaying) {
+                video.play();
+            }
+            video.removeEventListener('loadeddata', handleLoaded);
+        };
+        video.addEventListener('loadeddata', handleLoaded);
+    }
+
+    handleMouseEnterWrapper = () => {
+        this._adapter.setShowControls(true);
+    }
+
+    handleMouseLeaveWrapper = () => {
+        const { isPlaying } = this.getStates();
+        if (isPlaying) {
+            this._adapter.setShowControls(false);
+        }
+    }
+
+    handleFullscreenChange = () => {
+        const isFullScreen = this.checkFullScreen();
+        if (isFullScreen) {
+            document.addEventListener('mousemove', this.handleMouseMove);
+        } else {
+            // according to the exit fullScreen has two way, Esc && click the button
+            // so we need to restore scroll position after exiting fullscreen
+            if (this.scrollPosition) {
+                setTimeout(() => {
+                    window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);
+                    this.scrollPosition = null;
+                }, 0);
+            }
+            document.removeEventListener('mousemove', this.handleMouseMove);
+        }
+    }
+
+    registerEvent = () => {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        document.addEventListener('keydown', (e) => this.handleBodyKeyDown(e));
+        document.addEventListener('fullscreenchange', this.handleFullscreenChange);
+        video.addEventListener('leavepictureinpicture', this.handleLeavePictureInPicture);
+    }
+
+    unregisterEvent = () => {
+        const video = this._adapter.getVideo();
+        if (!video) return;
+        document.removeEventListener('keydown', (e) => this.handleBodyKeyDown(e));
+        document.removeEventListener('fullscreenchange', this.handleFullscreenChange);
+        video.removeEventListener('leavepictureinpicture', this.handleLeavePictureInPicture);
+    }
+
+    handleBodyKeyDown(e: KeyboardEvent) {
+        const { currentTime, volume } = this.getStates();
+        const { seekTime } = this.getProps();
+        if (e.key === ' ') {
+            this.handlePlayOrPause();
+        // } else if (e.key === 'ArrowUp') {
+        //     this.handleVolumeChange(volume + numbers.DEFAULT_VOLUME_STEP);
+        // } else if (e.key === 'ArrowDown') { 
+        //     this.handleVolumeChange(volume - numbers.DEFAULT_VOLUME_STEP);   
+        } else if (e.key === 'ArrowLeft') {
+            this.handleTimeChange(currentTime - seekTime);
+        } else if (e.key === 'ArrowRight') {
+            this.handleTimeChange(currentTime + seekTime);
+        }
+    }
+} 

+ 136 - 0
packages/semi-foundation/videoPlayer/progressFoundation.ts

@@ -0,0 +1,136 @@
+import BaseFoundation, { DefaultAdapter } from '../base/foundation';
+
+export interface MarkerListItem {
+    start: number;
+    end: number;
+    title: string;
+    width: string;
+    left: string
+}
+
+export interface Marker {
+    start: number;
+    title: string
+}
+
+export interface VideoProgressAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    getSliderRef: () => HTMLDivElement | null;
+    getMarkersList: () => MarkerListItem[];
+    setIsDragging: (isDragging: boolean) => void;
+    setIsHandleHovering: (isHandleHovering: boolean) => void;
+    setActiveIndex: (activeIndex: number) => void;
+    setMovingInfo: (movingInfo: { progress: number; offset: number; value: number } | null) => void
+}
+
+export default class VideoProgressFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<VideoProgressAdapter<P, S>, P, S> {
+    constructor(adapter: VideoProgressAdapter<P, S>) {
+        super({ ...adapter });
+    }
+
+    handleDocumentMouseMove = (e: MouseEvent) => {
+        const { isDragging } = this.getStates();
+        if (isDragging) {
+            this.handleMouseEvent(e, true);
+        }
+    };
+
+    handleDocumentMouseUp = () => {
+        const { isDragging } = this.getStates();
+        if (isDragging) {
+            this._adapter.setIsDragging(false);
+        }
+        document.removeEventListener('mousemove', this.handleDocumentMouseMove);
+        document.removeEventListener('mouseup', this.handleDocumentMouseUp);
+    };
+
+    handleMouseDown = (e: any) => {
+        this._adapter.setIsDragging(true);
+        this.handleMouseEvent(e, true);
+        document.addEventListener('mousemove', this.handleDocumentMouseMove);
+        document.addEventListener('mouseup', this.handleDocumentMouseUp);
+    };
+
+    handleMouseUp = () => {
+        const { isDragging } = this.getStates();
+        if (isDragging) {
+            this._adapter.setIsDragging(false);
+        }
+    };
+
+    handleMouseEvent = (e: any, shouldSetValue: boolean = true) => {
+        const { isDragging } = this.getStates();
+        const { onChange, max } = this.getProps();
+        const sliderRef = this._adapter.getSliderRef();
+        if (!sliderRef) return;
+        const rect = sliderRef.getBoundingClientRect();
+        const offset = (e.clientX - rect.left);
+        const total = rect.width;
+        const percentage = Math.min(Math.max(offset / total, 0), 1);
+        const value = percentage * max;
+        
+        if (shouldSetValue && (isDragging || e.type === 'mousedown')) {
+            this.setActiveIndex(value);
+            onChange(value);
+        }
+
+        this._adapter.setMovingInfo({
+            progress: percentage,
+            offset: offset - rect.width / 2,
+            value
+        });
+    };
+
+    handleSliderMouseEnter = (index: number) => {
+        const { value: currentValue } = this.getProps();
+        const markersList = this._adapter.getMarkersList();
+        const currentSlider = markersList[index];
+        if (currentSlider.start < currentValue && currentSlider.end > currentValue) {
+            this._adapter.setIsHandleHovering(true);
+        } else {
+            this._adapter.setIsHandleHovering(false);
+        }
+    }
+
+    handleSliderMouseLeave = (index: number) => {
+        const { value: currentValue } = this.getProps();
+        const markersList = this._adapter.getMarkersList();
+        const currentSlider = markersList[index];
+        if (currentSlider.start < currentValue && currentSlider.end > currentValue) {
+            this._adapter.setIsHandleHovering(false);
+        }
+    }
+
+    setActiveIndex = (currentValue: number) => {
+        const markersList = this._adapter.getMarkersList();
+        markersList.map((marker: MarkerListItem, index: number) => {
+            if (currentValue < marker.end && currentValue > marker.start) {
+                this._adapter.setIsHandleHovering(true);
+                this._adapter.setActiveIndex(index);
+            }
+        });
+    }
+
+    getValueWidth = (marker: MarkerListItem, value: number) => {
+        const { start, end } = marker;
+        if (value > end) {
+            return 'calc(100% - 2px)';
+        } else if (value < start) {
+            return '0%';
+        } else {
+            return `${(value - start) / (end - start) * 100}%`;
+        }
+    }
+
+    // Get the width of the video being played
+    getPlayedWidth = (marker: MarkerListItem) => {
+        const { value: currentValue } = this.getProps();
+        return this.getValueWidth(marker, currentValue);
+    }
+
+    getLoadedWidth = (marker: MarkerListItem) => {
+        const { bufferedValue } = this.getProps();
+        return this.getValueWidth(marker, bufferedValue);
+    }
+
+
+}   

+ 75 - 0
packages/semi-foundation/videoPlayer/variables.scss

@@ -0,0 +1,75 @@
+// Color
+$color-videoPlayer_theme_dark-text: var(--semi-color-bg-0); // dark theme 字体色
+$color-videoPlayer_theme_dark-bg: rgba(var(--semi-grey-8), 1); // dark theme 背景色
+$color-videoPlayer_theme_light-text: rgba(var(--semi-grey-8), 1); // light theme 字体色
+$color-videoPlayer_theme_light-bg: var(--semi-color-disabled-bg); // light theme 背景色
+$color-videoPlayer_pause-bg: var(--semi-color-text-1); // 暂停按钮颜色
+$color-videoPlayer_notification-bg: var(--semi-color-overlay-bg); // notification 背景色
+$color-videoPlayer_notification-text: var(--semi-color-default); // notification 字体色
+$color-videoPlayer_controls-bg: rgba(28, 31, 35, 0.8); // 控制栏背景色
+$color-videoPlayer_controls_item-bg: var(--semi-color-overlay-bg); // 控制栏 item 背景色
+$color-videoPlayer_controls-text: #fff; // 控制栏文字色
+$color-videoPlayer_controls_item_popup-bg-default: var(--semi-color-overlay-bg); // 控制栏弹层背景色
+$color-videoPlayer_controls_item_popup-bg-hover: rgba(67, 68, 74, 1); // 控制栏弹层背景色 - hover
+$color-videoPlayer_controls_popup_item-text-default: rgba(#fff, 0.7); // 控制栏文字色
+$color-videoPlayer_controls_popup_item-text-active: var(--semi-color-primary); // 控制栏弹层文字色 - 选中
+$color-videoPlayer_progress_bar-bg-played: var(--semi-color-primary); // 进度条已播放部分背景色
+$color-videoPlayer_progress_bar-bg-loaded: rgba(var(--semi-grey-3), 1); // 进度条已加载部分背景色
+$color-videoPlayer_progress_bar-bg-unplayed: rgba(var(--semi-grey-5), 1); // 进度条未播放部分背景色
+$color-videoPlayer_progress_bar_handle-bg: #fff; // handle 背景色
+$color-videoPlayer_progress_bar_handle-border: var(--semi-color-primary); // handle 边框色
+$color-videoPlayer_progress_bar_handle-shadow: var(--semi-color-shadow); // handle 阴影色
+
+// Width/Height
+$height-videoPlayer_progress_bar_hotSpot-default: 20px; // 进度条热区 default 高度
+$height-videoPlayer_progress_bar-default: 4px; // 进度条 default 高度
+$height-videoPlayer_progress_bar-hover: 10px; // 进度条 hover 高度
+$height-videoPlayer_progress_bar_handle: 16px; // 进度条 handle 高度
+$height-videoPlayer_controls_menu-default: 56px; // 控制栏 default 高度
+$height-videoPlayer_controls_volume-default: 160px; // 控制栏音量 default 高度
+$height-videoPlayer_controls_popup-default: 24px; // 控制栏弹层类元素 default 高度
+$height-videoPlayer_controls_popup_item-default: 32px; // 控制栏弹层类元素 item default 高度
+$width-videoPlayer_controls_volume-default: 40px; // 控制栏音量 default 宽度
+$width-videoPlayer_controls_popup_item-default: 50px; // 控制栏弹层类元素 default 宽度
+$width-videoPlayer_controls_popup-default: 48px; // 控制栏弹层 default 宽度
+
+// Spacing
+$spacing-videoPlayer-progress_bar_chapter-marginRight: 2px; // 进度条分节间距
+$spacing-videoPlayer_notification-bottom: 22px; // notification 距离菜单栏距离
+$spacing-videoPlayer_notification-left: 8px; // notification 距离菜单栏左侧距离
+$spacing-videoPlayer_notification_text-paddingY: 8px; // notification 文字垂直方向 padding
+$spacing-videoPlayer_notification_text-paddingX: 12px; // notification 文字水平方向 padding
+$spacing-videoPlayer-controls-padding: $spacing-base-tight $spacing-base; // 控制栏 padding
+$spacing-videoPlayer-controls_item-gap: $spacing-tight; // 控制栏 item 间距
+$spacing-videoPlayer-controls_time-paddingX: 8px; // 控制栏时间 paddingX
+$spacing-videoPlayer-controls_volume_title-paddingX: 10px; // 控制栏音量标题文字 paddingX
+$spacing-videoPlayer-controls_volume_title-paddingY: 0px; // 控制栏音量标题文字 paddingX
+$spacing-videoPlayer-controls_volume_popup-paddingY: 0px; // 控制栏音量弹层 paddingX
+$spacing-videoPlayer-controls_volume_popup-paddingX: $spacing-extra-tight; // 控制栏音量弹层 paddingX
+$spacing-videoPlayer-controls_popup-paddingX: $spacing-tight; // 控制栏弹层元素 paddingX
+$spacing-videoPlayer-controls_popup-paddingY: 0px; // 控制栏弹层元素 paddingY
+$spacing-videoPlayer-progress_bar_wrapper-marginX: $spacing-tight; // 进度条 wrapper marginX
+$spacing-videoPlayer-progress_bar_wrapper-marginY: 0px; // 进度条 wrapper marginY
+$spacing-videoPlayer-progress_bar_tooltip-top: 6px; // 进度条 Tooltip top 值
+$spacing-videoPlayer_progress_bar_handle-top: 15px; // 进度条 handle top 值
+$spacing-videoPlayer_error_svg-marginBottom: 12px; // error svg marginBottom
+
+// Radius
+$radius-videoPlayer_notification: 3px; // notification 圆角
+$radius-videoPlayer_progress_bar_handle: 50%; // 进度条 handle 圆角
+$radius-videoPlayer_progress_bar: 999px; // 进度条圆角
+$radius-videoPlayer_controls_item: 3px; // 控制栏 item 圆角
+$radius-videoPlayer_controls_popup: 4px; // 控制栏弹层圆角
+
+// Font
+$font-videoPlayer_notification-fontSize: $font-size-regular; // notification 字体大小
+$font-videoPlayer_controls_item-fontSize: $font-size-small; // 控制栏 item 字体大小
+$font-videoPlayer_controls_time_text-fontSize: $font-size-regular; // 控制栏时间字体大小
+$font-videoPlayer_error-fontSize: $font-size-regular; // error 字体大小
+$font-videoPlayer_notification-lineHeight: 20px; // notification 行高
+$font-videoPlayer_controls_popup_item-lineHeight: 16px; // 控制栏 item 行高
+$font-videoPlayer_controls_popup_item-fontWeight: 600; // 控制栏 item 字体粗细
+$font-videoPlayer_error-fontWeight: 600; // error 字体粗细
+
+
+

+ 323 - 0
packages/semi-foundation/videoPlayer/videoPlayer.scss

@@ -0,0 +1,323 @@
+@import './variables.scss';
+@import './animation.scss';
+
+$module: #{$prefix}-videoPlayer;
+
+.#{$module} {
+    position: relative;
+    display: inline-block;
+    width: 100%;
+    height: 100%;
+
+    // Hide native controls
+    ::-webkit-media-controls {
+        display: none;
+    }
+    
+    &-wrapper {
+        width: 100%;
+        height: 100%;
+        position: relative;
+        line-height: 0;
+
+        video {
+            width: 100%;
+            height: 100%;
+            object-fit: contain;
+        }
+
+        &-dark {
+            background-color: $color-videoPlayer_theme_dark-bg;
+            color: $color-videoPlayer_theme_dark-text;
+        }
+
+        &-light {
+            background-color: $color-videoPlayer_theme_light-bg;
+            color: $color-videoPlayer_theme_light-text;
+        }
+    
+    }
+
+    &-pause {
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        @include all-center;
+        pointer-events: none;
+
+        svg {
+            font-size: 88px;
+            color: $color-videoPlayer_pause-bg;
+            pointer-events: none;
+        }
+    }
+
+    &-error {
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        @include all-center;
+        flex-direction: column;
+        font-weight: $font-videoPlayer_error-fontWeight;
+        font-size: $font-videoPlayer_error-fontSize;
+
+        &-svg {
+            margin-bottom: $spacing-videoPlayer_error_svg-marginBottom;
+        }
+
+        &-dark {
+            background-color: $color-videoPlayer_theme_dark-bg;
+            color: $color-videoPlayer_theme_dark-text;
+
+            path {
+                fill: $color-videoPlayer_theme_dark-text;
+            }
+            
+        }
+
+        &-light {
+            background-color: $color-videoPlayer_theme_light-bg;
+            color: $color-videoPlayer_theme_light-text;
+
+            path {
+                fill: $color-videoPlayer_theme_light-text;
+            }
+        }
+    }
+
+    &-poster {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        pointer-events: none;
+
+        &-hide {
+            opacity: 0;
+        }
+    }
+
+    &-notification {
+        position: absolute;
+        bottom: $height-videoPlayer_controls_menu-default + $spacing-videoPlayer_notification-bottom;
+        left: $spacing-videoPlayer_notification-left;
+        text-align: center;
+        background-color: $color-videoPlayer_notification-bg;
+        color: $color-videoPlayer_notification-text;
+        padding: $spacing-videoPlayer_notification_text-paddingY $spacing-videoPlayer_notification_text-paddingX;
+        line-height: $font-videoPlayer_notification-lineHeight;
+        border-radius: $radius-videoPlayer_notification;
+        font-size: $font-videoPlayer_notification-fontSize;
+    }
+
+    &-mirror {
+        video {
+            transform: rotateX(0deg) rotateY(180deg);
+        }
+    }
+
+    &-controls {
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        height: fit-content;
+        opacity: 1;
+        transition: opacity $animation_duration-videoPlayer_controls-show ease-in-out;
+        z-index: 1;
+
+        &-hide {
+            opacity: 0;
+        }
+
+        &-menu {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            box-sizing: border-box;
+            height: $height-videoPlayer_controls_menu-default;
+            background-color: $color-videoPlayer_controls-bg;
+            padding: $spacing-videoPlayer-controls-padding;
+
+            &-left,
+            &-right {
+                display: flex;
+                align-items: center;
+            }
+
+            &-item {
+                margin-right: $spacing-videoPlayer-controls_item-gap;
+            }
+
+            &-button {
+                svg {
+                    color: $color-videoPlayer_controls-text;
+                }
+            }
+        }
+
+        &-time {
+            color: $color-videoPlayer_controls-text;
+            font-size: $font-videoPlayer_controls_time_text-fontSize;
+            margin-right: $spacing-videoPlayer-controls_item-gap;
+            padding: 0 $spacing-videoPlayer-controls_time-paddingX;
+            font-variant-numeric: tabular-nums; 
+        }
+
+        &-popover {
+            background-color: transparent;
+        }
+
+        &-volume {
+            box-sizing: border-box;
+            width: $width-videoPlayer_controls_volume-default;
+            height: $height-videoPlayer_controls_volume-default;
+            background-color: $color-videoPlayer_controls_item-bg;
+            color: $color-videoPlayer_controls_popup_item-text-default;
+            border-radius: $radius-videoPlayer_controls_popup;
+            padding: $spacing-videoPlayer-controls_volume_popup-paddingY $spacing-videoPlayer-controls_volume_popup-paddingX;
+            line-height: $font-videoPlayer_controls_popup_item-lineHeight;
+            font-size: $font-videoPlayer_controls_item-fontSize;
+            user-select: none;
+
+            &-title {
+                padding: $spacing-videoPlayer-controls_volume_title-paddingX $spacing-videoPlayer-controls_volume_title-paddingY;
+                text-align: center;
+            }
+        }
+
+        &-popup {
+            @include all-center;
+            width: $width-videoPlayer_controls_popup_item-default;
+            height: $height-videoPlayer_controls_popup-default;
+            background-color: $color-videoPlayer_controls_item_popup-bg-default;
+            color: $color-videoPlayer_controls-text;
+            font-weight: $font-videoPlayer_controls_popup_item-fontWeight;
+            font-size: $font-videoPlayer_controls_item-fontSize;
+            line-height: $font-videoPlayer_controls_popup_item-lineHeight;
+            border-radius: $radius-videoPlayer_controls_item;
+            cursor: pointer;
+
+            &-menu {
+                width: $width-videoPlayer_controls_popup-default;
+                background-color: $color-videoPlayer_controls_item_popup-bg-default;
+                border-radius: $radius-videoPlayer_controls_popup;
+
+                &-item {
+                    @include all-center;
+                    padding: $spacing-videoPlayer-controls_popup-paddingY $spacing-videoPlayer-controls_popup-paddingX;
+                    height: $height-videoPlayer_controls_popup_item-default;
+                    color: $color-videoPlayer_controls-text;
+                    font-size: $font-videoPlayer_controls_item-fontSize;
+
+                    &:hover {
+                        background-color: $color-videoPlayer_controls_item_popup-bg-hover !important;
+                    }
+                }
+
+                .#{$prefix}-dropdown-item-active {
+                    color: $color-videoPlayer_controls_popup_item-text-active;
+                    font-weight: $font-videoPlayer_controls_popup_item-fontWeight;
+                    cursor: pointer;
+                }
+            }
+        }
+
+    }
+
+    &-progress {
+        position: relative;
+        height: $height-videoPlayer_progress_bar_hotSpot-default;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        cursor: pointer;
+        margin: $spacing-videoPlayer-progress_bar_wrapper-marginY $spacing-videoPlayer-progress_bar_wrapper-marginX;
+
+        &-markers {
+            position: absolute;
+            width: 100%;
+            height: 100%;
+        }
+
+        &-tooltip {
+            top: $spacing-videoPlayer-progress_bar_tooltip-top;
+
+            &-content {
+                @include all-center;
+            }
+        }
+
+        &-slider {
+            position: absolute;
+            width: 100%;
+            height: 100%;
+            left: 0;
+            bottom: 0;
+            border-radius: $radius-videoPlayer_progress_bar;
+
+            &-active, &:hover {
+                .#{$module}-progress-slider-list, 
+                .#{$module}-progress-slider-played,
+                .#{$module}-progress-slider-buffered {
+                    height: $height-videoPlayer_progress_bar-hover;
+                    transition: transform $animation_duration-videoPlayer_slider-in;
+                    transition-timing-function: $animation_function-videoPlayer_slider-in;
+                }
+            }
+
+            &-list {
+                position: absolute;
+                bottom: 0;
+                left: 0;
+                background-color: $color-videoPlayer_progress_bar-bg-unplayed;
+                height: $height-videoPlayer_progress_bar-default;
+                width: calc(100% - $spacing-videoPlayer-progress_bar_chapter-marginRight);
+                margin-right: $spacing-videoPlayer-progress_bar_chapter-marginRight;
+                border-radius: $radius-videoPlayer_progress_bar;
+                transition: transform $animation_duration-videoPlayer_slider-in;
+                transition-timing-function: $animation_function-videoPlayer_slider-out;
+            }
+
+            &-played {
+                position: absolute;
+                bottom: 0;
+                left: 0;
+                background-color: $color-videoPlayer_progress_bar-bg-played;
+                height: $height-videoPlayer_progress_bar-default;
+                border-radius: $radius-videoPlayer_progress_bar;
+                transition: transform $animation_duration-videoPlayer_slider-in;
+                transition-timing-function: $animation_function-videoPlayer_slider-out;
+            }
+
+            &-buffered {
+                position: absolute;
+                bottom: 0;
+                left: 0;
+                background-color: $color-videoPlayer_progress_bar-bg-loaded;
+                height: $height-videoPlayer_progress_bar-default;
+                border-radius: $radius-videoPlayer_progress_bar;
+                transition: transform $animation_duration-videoPlayer_slider-in;
+                transition-timing-function: $animation_function-videoPlayer_slider-out;
+            }
+        }
+
+        &-handle {
+            box-sizing: border-box;
+            position: absolute;
+            width: $height-videoPlayer_progress_bar_handle;
+            height: $height-videoPlayer_progress_bar_handle;
+            background-color: $color-videoPlayer_progress_bar_handle-bg;
+            border: 1px solid $color-videoPlayer_progress_bar_handle-border;
+            box-shadow: 0px 0px 4px 0px $color-videoPlayer_progress_bar_handle-shadow;
+            border-radius: $radius-videoPlayer_progress_bar_handle;
+            top: $spacing-videoPlayer_progress_bar_handle-top;
+        }
+        
+    }
+} 

+ 1 - 1
packages/semi-icons-lab/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-icons-lab",
-  "version": "2.79.0",
+  "version": "2.81.0",
   "description": "semi icons lab",
   "keywords": [
     "semi",

+ 1 - 1
packages/semi-icons/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-icons",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "semi icons",
     "keywords": [
         "semi",

+ 24 - 0
packages/semi-icons/src/icons/IconMiniPlayer.tsx

@@ -0,0 +1,24 @@
+import * as React from 'react';
+import { convertIcon } from '../components/Icon';
+function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
+    return (
+        <svg 
+            viewBox="0 0 24 24"
+            fill="none"
+            xmlns="http://www.w3.org/2000/svg"
+            width="1em"
+            height="1em"
+            focusable={false}
+            aria-hidden={true}
+            {...props}
+        >
+            <path d="M15.501 22.0005C14.6725 22.0005 14.001 21.3289 14.001 20.5005V15.5005C14.001 14.6721 14.6725 14.0005 15.501 14.0005H20.501C21.3294 14.0005 22.001 14.6721 22.001 15.5005V20.5005C22.001 21.3289 21.3294 22.0005 20.501 22.0005L15.501 22.0005Z" fill="currentColor"/>
+            <path d="M5.50098 22.0005C3.56798 22.0005 2.00098 20.4335 2.00098 18.5005V15.5005V5.50049C2.00098 3.56749 3.56798 2.00049 5.50098 2.00049H15.501H18.501C20.434 2.00049 22.001 3.56749 22.001 5.50049V9.75038C22.001 10.5788 21.3294 11.2504 20.501 11.2504C19.6726 11.2504 19.001 10.5788 19.001 9.75038V5.50049C19.001 5.22435 18.7771 5.00049 18.501 5.00049H15.501H5.50098C5.22483 5.00049 5.00098 5.22435 5.00098 5.50049V15.5005V18.5005C5.00098 18.7766 5.22483 19.0005 5.50098 19.0005H9.74999C10.5784 19.0005 11.25 19.6721 11.25 20.5005C11.25 21.3289 10.5784 22.0005 9.74999 22.0005H5.50098Z" fill="currentColor"/>
+            <path fillRule="evenodd" clipRule="evenodd" d="M7.52045 7.18658C7.12993 7.57711 7.12994 8.21026 7.52046 8.60078L10.4702 11.5505L9.64175 11.5505C9.08946 11.5505 8.64175 11.9982 8.64175 12.5505C8.64175 13.1028 9.08946 13.5505 9.64175 13.5505L12.8844 13.5505C13.4367 13.5505 13.8844 13.1028 13.8844 12.5505L13.8844 9.30788C13.8844 8.75559 13.4367 8.30788 12.8844 8.30788C12.3321 8.30788 11.8844 8.7556 11.8844 9.30788L11.8844 10.1363L8.93467 7.18657C8.54414 6.79604 7.91097 6.79605 7.52045 7.18658V7.18658Z" fill="currentColor"/>
+        </svg>
+    );
+}
+const IconComponent = convertIcon(SvgComponent, 'mini_player');
+export default IconComponent;
+
+

+ 1 - 0
packages/semi-icons/src/icons/index.ts

@@ -264,6 +264,7 @@ export { default as IconMenu } from './IconMenu';
 export { default as IconMicrophone } from './IconMicrophone';
 export { default as IconMicrophoneOff } from './IconMicrophoneOff';
 export { default as IconMinimize } from './IconMinimize';
+export { default as IconMiniPlayer } from './IconMiniPlayer';
 export { default as IconMinus } from './IconMinus';
 export { default as IconMinusCircle } from './IconMinusCircle';
 export { default as IconMinusCircleStroked } from './IconMinusCircleStroked';

+ 1 - 1
packages/semi-illustrations/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-illustrations",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "semi illustrations",
     "keywords": [
         "semi",

+ 1 - 1
packages/semi-json-viewer-core/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-json-viewer-core",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "",
     "main": "lib/index.js",
     "module": "lib/index.js",

+ 2 - 2
packages/semi-next/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-next",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "Plugin that support Semi Design in Next.js",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",
@@ -22,7 +22,7 @@
         "typescript": "^4"
     },
     "dependencies": {
-        "@douyinfe/semi-webpack-plugin": "2.79.0"
+        "@douyinfe/semi-webpack-plugin": "2.81.0"
     },
     "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }

+ 1 - 1
packages/semi-rspack/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-rspack-plugin",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "",
     "homepage": "",
     "license": "MIT",

+ 1 - 1
packages/semi-scss-compile/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-scss-compile",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "compile semi scss to css",
     "author": "[email protected]",
     "license": "MIT",

+ 1 - 1
packages/semi-theme-default/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-theme-default",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "semi-theme-default",
     "keywords": [
         "semi-theme",

+ 96 - 0
packages/semi-ui/cascader/_story/cascader.stories.jsx

@@ -2517,4 +2517,100 @@ export const PrefixSuffix = () => {
       />
     </>
   )
+}
+
+export const Fixed2831 = () => {
+    const [value, setValue] = useState(undefined);
+    const [productAndBusinessData, setProductAndBusinessData] = useState([
+        {
+        label: 'TCS Manager',
+        value: 'TCS Manager',
+        children: [
+            {
+            label: '0',
+            value: '0',
+            },
+            {
+            label: '1',
+            value: '1',
+            },
+            {
+            label: '2',
+            value: '2',
+            },
+            {
+            label: '3',
+            value: '3',
+            },
+        ],
+        },
+        {
+        label: 'AAA Manager',
+        value: 'AAA Manager',
+        children: [
+            {
+            label: '4',
+            value: '4',
+            },
+            {
+            label: '5',
+            value: '5',
+            },
+            {
+            label: '6',
+            value: '6',
+            },
+            {
+            label: '7',
+            value: '7',
+            },
+        ],
+        },
+    ]);
+    return (
+        <Cascader
+            value={value}
+            field="ss"
+            placeholder={('tcs_manager_business_info')}
+            treeData={productAndBusinessData}
+            showNext="hover"
+            changeOnSelect
+            showClear
+            autoAdjustOverflow={false}
+            onChange={(value) => {setValue(value)}}
+            loadData={selectedOpt => {
+                if (!selectedOpt) {
+                return Promise.resolve();
+                }
+                const targetOpt = selectedOpt[selectedOpt.length - 1];
+                const { value } = targetOpt;
+                if (!value) {
+                return Promise.resolve();
+                }
+                return new Promise( (res) => {
+                    setTimeout(() => {
+                        setProductAndBusinessData(origin => {
+                        const newData = origin.map(i => {
+                        const children = (i.children || []).map((j, jIndex) => {
+                            if (j.children || j.value !== value) {
+                            return { ...j };
+                            }
+                            const subChildren = Array.from({ length: 5 }).map((i, index) => ({
+                            label: jIndex + '' + index,
+                            value: jIndex + '' + index,
+                            disabled: false,
+                            isLeaf: true,
+                            }));
+                            return { ...j, children: subChildren };
+                        });
+                        return { ...i, children };
+                        });
+                        return newData;
+                    });
+                        res();
+                    }, 2000);
+                });
+            }}
+            />
+    );
 }

+ 2 - 2
packages/semi-ui/form/interface.ts

@@ -123,9 +123,9 @@ export interface BaseFormProps <Values extends Record<string, any> = any> extend
     labelAlign?: 'left' | 'right';
     labelCol?: Record<string, any>;
     wrapperCol?: Record<string, any>;
-    render?: (internalProps: FormFCChild) => React.ReactNode;
+    render?: (internalProps: FormFCChild<Values>) => React.ReactNode;
     component?: React.FC<any> | React.ComponentClass<any>;
-    children?: React.ReactNode | ((internalProps: FormFCChild) => React.ReactNode);
+    children?: React.ReactNode | ((internalProps: FormFCChild<Values>) => React.ReactNode);
     autoScrollToError?: boolean | ScrollIntoViewOptions;
     disabled?: boolean;
     showValidateIcon?: boolean;

+ 1 - 0
packages/semi-ui/index.ts

@@ -128,3 +128,4 @@ export { default as DragMove } from './dragMove';
 export { default as Cropper } from './cropper';
 export { default as AudioPlayer } from './audioPlayer';
 export { default as UserGuide } from './userGuide';
+export { default as VideoPlayer } from './videoPlayer';

+ 0 - 1
packages/semi-ui/input/_story/input.stories.jsx

@@ -1067,4 +1067,3 @@ export const FixTextAreaAutoFocus = () => {
     </div>
   )
 };
-

+ 2 - 0
packages/semi-ui/input/index.tsx

@@ -503,6 +503,8 @@ class Input extends BaseComponent<InputProps, InputState> {
             onKeyUp: e => this.foundation.handleKeyUp(e),
             onKeyDown: e => this.foundation.handleKeyDown(e),
             onKeyPress: e => this.foundation.handleKeyPress(e),
+            onCompositionStart: this.foundation.handleCompositionStart,
+            onCompositionEnd: this.foundation.handleCompositionEnd,
             value: inputValue,
         };
         if (!isFunction(getValueLength)) {

+ 2 - 0
packages/semi-ui/input/textarea.tsx

@@ -301,6 +301,8 @@ class TextArea extends BaseComponent<TextAreaProps, TextAreaState> {
             onBlur: (e: React.FocusEvent<HTMLTextAreaElement>) => this.foundation.handleBlur(e.nativeEvent),
             onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => this.foundation.handleKeyDown(e),
             value: value === null || value === undefined ? '' : value,
+            onCompositionStart: this.foundation.handleCompositionStart,
+            onCompositionEnd: this.foundation.handleCompositionEnd,
         };
         if (!isFunction(getValueLength)) {
             (itemProps as any).maxLength = maxLength;

+ 11 - 0
packages/semi-ui/locale/interface.ts

@@ -194,5 +194,16 @@ export interface Locale {
         search: string;
         replace: string;
         replaceAll: string
+    };
+    VideoPlayer: {
+        rateChange: string;
+        qualityChange: string;
+        routeChange: string;
+        mirror: string;
+        cancelMirror: string;
+        loading: string;
+        stall: string;
+        noResource: string;
+        videoError: string
     }
 }

+ 11 - 0
packages/semi-ui/locale/source/ar.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'استبدل',
         replaceAll: 'استبدل الكل',
     },
+    VideoPlayer: {
+        rateChange: 'تحويل السرعة إلى ${rate}',
+        qualityChange: 'تحويل الجودة إلى ${quality}',
+        routeChange: 'تحويل المسار إلى ${route}',
+        mirror: 'المرآة',
+        cancelMirror: 'إلغاء المرآة',
+        loading: 'جار التحميل...',
+        stall: 'فشل التحميل',
+        noResource: 'لا يوجد مورد',
+        videoError: 'خطأ في تحميل الفيديو'
+    }
 };
 
 // [i18n-Arabic]

+ 11 - 0
packages/semi-ui/locale/source/de.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'Ersetzen',
         replaceAll: 'Alle ersetzen',
     },
+    VideoPlayer: {
+        rateChange: 'Geschwindigkeit auf ${rate} wechseln',
+        qualityChange: 'Qualität auf ${quality} wechseln',
+        routeChange: 'Route auf ${route} wechseln',
+        mirror: 'Spiegel',
+        cancelMirror: 'Spiegelung aufheben',
+        loading: 'Wird geladen...',
+        stall: 'Laden fehlgeschlagen',
+        noResource: 'Keine Ressource',
+        videoError: 'Video-Ladefehler'
+    }
 };
 
 // [i18n-German]

+ 11 - 0
packages/semi-ui/locale/source/en_GB.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'Replace',
         replaceAll: 'Replace All',
     },
+    VideoPlayer: {
+        rateChange: 'Switch rate to ${rate}',
+        qualityChange: 'Switch quality to ${quality}',
+        routeChange: 'Switch route to ${route}',
+        mirror: 'Mirror',
+        cancelMirror: 'Cancel mirror',
+        loading: 'Loading...',
+        stall: 'Loading failed',
+        noResource: 'No resource',
+        videoError: 'Video load error'
+    }
 };
 
 // [i18n-English(GB)]

+ 11 - 0
packages/semi-ui/locale/source/en_US.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'Replace',
         replaceAll: 'Replace All',
     },
+    VideoPlayer: {
+        rateChange: 'Switch rate to ${rate}',
+        qualityChange: 'Switch quality to ${quality}',
+        routeChange: 'Switch route to ${route}',
+        mirror: 'Mirror',
+        cancelMirror: 'Cancel mirror',
+        loading: 'Loading...',
+        stall: 'Loading failed',
+        noResource: 'No resource',
+        videoError: 'Video load error',
+    }
 };
 
 // [i18n-English(US)]

+ 11 - 0
packages/semi-ui/locale/source/es.ts

@@ -200,6 +200,17 @@ const locale: Locale = {
         replace: 'Reemplazar',
         replaceAll: 'Reemplazar todo',
     },
+    VideoPlayer: {
+        rateChange: 'Cambiar velocidad a ${rate}',
+        qualityChange: 'Cambiar calidad a ${quality}',
+        routeChange: 'Cambiar ruta a ${route}',
+        mirror: 'Espejo',
+        cancelMirror: 'Cancelar espejo',
+        loading: 'Cargando...',
+        stall: 'Carga fallida',
+        noResource: 'Sin recursos',
+        videoError: 'Error al cargar el video'
+    }
 };
 
 export default locale;

+ 11 - 0
packages/semi-ui/locale/source/fr.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'Remplacer',
         replaceAll: 'Remplacer tout',
     },
+    VideoPlayer: {
+        rateChange: 'Changer la vitesse à ${rate}',
+        qualityChange: 'Changer la qualité à ${quality}',
+        routeChange: 'Changer la route à ${route}',
+        mirror: 'Miroir',
+        cancelMirror: 'Annuler le miroir',
+        loading: 'Chargement...',
+        stall: 'Chargement échoué',
+        noResource: 'Aucune ressource',
+        videoError: 'Erreur de chargement de la vidéo'
+    }
 };
 
 // [i18n-French]

+ 11 - 0
packages/semi-ui/locale/source/id_ID.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'Ganti',
         replaceAll: 'Ganti Semua',
     },
+    VideoPlayer: {
+        rateChange: 'Ubah kecepatan ke ${rate}',
+        qualityChange: 'Ubah kualitas ke ${quality}',
+        routeChange: 'Ubah rute ke ${route}',
+        mirror: 'Cermin',
+        cancelMirror: 'Hapus cermin',
+        loading: 'Memuat...',
+        stall: 'Memuat gagal',
+        noResource: 'Tidak ada sumber',
+        videoError: 'Kesalahan memuat video'
+    }
 };
 
 // [i18n-Indonesia(ID)]

+ 11 - 0
packages/semi-ui/locale/source/it.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'Sostituisci',
         replaceAll: 'Sostituisci tutto',
     },
+    VideoPlayer: {
+        rateChange: 'Cambia velocità a ${rate}',
+        qualityChange: 'Cambia qualità a ${quality}',
+        routeChange: 'Cambia route a ${route}',
+        mirror: 'Specchio',
+        cancelMirror: 'Rimuovi specchio',
+        loading: 'Caricamento in corso...',
+        stall: 'Caricamento fallito',
+        noResource: 'Nessuna risorsa',
+        videoError: 'Errore di caricamento video'
+    }
 };
 
 // [i18n-Italian]

+ 11 - 0
packages/semi-ui/locale/source/ja_JP.ts

@@ -196,6 +196,17 @@ const local: Locale = {
         replace: '置換',
         replaceAll: 'すべて置換',
     },
+    VideoPlayer: {
+        rateChange: '速さを${rate}に変更',
+        qualityChange: '品質を${quality}に変更',
+        routeChange: 'ルートを${route}に変更',
+        mirror: '鏡像',
+        cancelMirror: '鏡像を解除',
+        loading: '読み込み中...',
+        stall: '読み込みに失敗しました',
+        noResource: 'リソースなし',
+        videoError: '動画の読み込みエラー'
+    }
 };
 
 // [i18n-Japan]

+ 11 - 0
packages/semi-ui/locale/source/ko_KR.ts

@@ -196,6 +196,17 @@ const local: Locale = {
         replace: '교체',
         replaceAll: '모두 교체',
     },
+    VideoPlayer: {
+        rateChange: '속도를 ${rate}로 변경',
+        qualityChange: '품질을 ${quality}로 변경',
+        routeChange: '경로를 ${route}로 변경',
+        mirror: '거울',
+        cancelMirror: '거울 해제',
+        loading: '로딩 중...',
+        stall: '로딩 실패',
+        noResource: '리소스 없음',
+        videoError: '비디오 로드 오류'
+    }
 };
 
 // [i18n-Korea]

+ 11 - 0
packages/semi-ui/locale/source/ms_MY.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'Ganti',
         replaceAll: 'Ganti Semua',
     },
+    VideoPlayer: {
+        rateChange: 'Ubah kecepatan ke ${rate}',
+        qualityChange: 'Ubah kualitas ke ${quality}',
+        routeChange: 'Ubah rute ke ${route}',
+        mirror: 'Cermin',
+        cancelMirror: 'Hapus cermin',
+        loading: 'Memuat...',
+        stall: 'Memuat gagal',
+        noResource: 'Tiada sumber',
+        videoError: 'Ralat memuatkan video'
+    }
 };
 
 // [i18n-Malaysia(MY)]

+ 11 - 0
packages/semi-ui/locale/source/nl_NL.ts

@@ -202,6 +202,17 @@ const local: Locale = {
         replace: 'Vervangen',
         replaceAll: 'Alle vervangen',
     },
+    VideoPlayer: {
+        rateChange: 'Verander snelheid naar ${rate}',
+        qualityChange: 'Verander kwaliteit naar ${quality}',
+        routeChange: 'Verander route naar ${route}',
+        mirror: 'Spiegel',
+        cancelMirror: 'Spiegel opheffen',
+        loading: 'Laden...',
+        stall: 'Laden mislukt',
+        noResource: 'Geen bron',
+        videoError: 'Fout bij laden video'
+    }
 };
 
 export default local;

+ 11 - 0
packages/semi-ui/locale/source/pl_PL.ts

@@ -203,6 +203,17 @@ const local: Locale = {
         replace: 'Zastąp',
         replaceAll: 'Zastąp wszystko',
     },
+    VideoPlayer: {
+        rateChange: 'Zmień prędkość na ${rate}',
+        qualityChange: 'Zmień jakość na ${quality}',
+        routeChange: 'Zmień ścieżkę na ${route}',
+        mirror: 'Lustrzane odbicie',
+        cancelMirror: 'Odwróć lustrzane odbicie',
+        loading: 'Ładowanie...',
+        stall: 'Ładowanie nie powiodło się',
+        noResource: 'Brak zasobu',
+        videoError: 'Błąd ładowania wideo'
+    }
 };
 
 export default local;

+ 11 - 0
packages/semi-ui/locale/source/pt_BR.ts

@@ -203,6 +203,17 @@ const local: Locale = {
         replace: 'Substituir',
         replaceAll: 'Substituir tudo',
     },
+    VideoPlayer: {
+        rateChange: 'Mudar velocidade para ${rate}',
+        qualityChange: 'Mudar qualidade para ${quality}',
+        routeChange: 'Mudar rota para ${route}',
+        mirror: 'Espelho',
+        cancelMirror: 'Remover espelho',
+        loading: 'Carregando...',
+        stall: 'Carregamento falhou',
+        noResource: 'Sem recurso',
+        videoError: 'Erro ao carregar vídeo'
+    }
 };
 
 // 葡萄牙语

+ 11 - 0
packages/semi-ui/locale/source/ro.ts

@@ -195,6 +195,17 @@ const local: Locale = {
         replace: 'Înlocuiește',
         replaceAll: 'Înlocuiește toate',
     },
+    VideoPlayer: {
+        rateChange: 'Schimbați viteza la ${rate}',
+        qualityChange: 'Schimbați calitatea la ${quality}',
+        routeChange: 'Schimbați ruta la ${route}',
+        mirror: 'Mirror',
+        cancelMirror: 'Anulează oglindirea',
+        loading: 'Se încarcă',
+        stall: 'Se încarcă',
+        noResource: 'Nicio resursă',
+        videoError: 'Eroare la încărcarea videoclipului'
+    },
 };
 
 // [i18n-Romanian] 罗马尼亚语

+ 11 - 0
packages/semi-ui/locale/source/ru_RU.ts

@@ -198,6 +198,17 @@ const local: Locale = {
         replace: 'Заменить',
         replaceAll: 'Заменить все',
     },
+    VideoPlayer: {
+        rateChange: 'Изменить скорость на ${rate}',
+        qualityChange: 'Изменить качество на ${quality}',
+        routeChange: 'Изменить маршрут на ${route}',
+        mirror: 'Зеркало',
+        cancelMirror: 'Отменить зеркало',
+        loading: 'Загрузка...',
+        stall: 'Загрузка не удалась',
+        noResource: 'Нет ресурса',
+        videoError: 'Ошибка загрузки видео'
+    }
 };
 
 // [i18n-Russia] 俄罗斯语

+ 11 - 0
packages/semi-ui/locale/source/sv_SE.ts

@@ -200,6 +200,17 @@ const local: Locale = {
         replace: 'Ersätt',
         replaceAll: 'Ersätt alla',
     },
+    VideoPlayer: {
+        rateChange: 'Ändra hastighet till ${rate}',
+        qualityChange: 'Ändra kvalitet till ${quality}',
+        routeChange: 'Ändra väg till ${route}',
+        mirror: 'Spegel',
+        cancelMirror: 'Ta bort spegel',
+        loading: 'Läser in...',
+        stall: 'Läsning misslyckades',
+        noResource: 'Ingen resurs',
+        videoError: 'Fel vid inläsning av video'
+    }
 };
 
 export default local;

+ 11 - 0
packages/semi-ui/locale/source/th_TH.ts

@@ -199,6 +199,17 @@ const local: Locale = {
         replace: 'แทนที่',
         replaceAll: 'แทนที่ทั้งหมด',
     },
+    VideoPlayer: {
+        rateChange: 'เปลี่ยนความเร็วเป็น ${rate}',
+        qualityChange: 'เปลี่ยนคุณภาพเป็น ${quality}',
+        routeChange: 'เปลี่ยนเส้นทางเป็น ${route}',
+        mirror: 'กลับหน้า',
+        cancelMirror: 'ยกเลิกกลับหน้า',
+        loading: 'กำลังโหลด...',
+        stall: 'กำลังโหลดล้มเหลว',
+        noResource: 'ไม่มีทรัพยากร',
+        videoError: 'เกิดข้อผิดพลาดในการโหลดวิดีโอ'
+    }
 };
 
 // [i18n-Thai]

+ 11 - 0
packages/semi-ui/locale/source/tr_TR.ts

@@ -196,6 +196,17 @@ const local: Locale = {
         replace: 'Değiştir',
         replaceAll: 'Tümünü değiştir',
     },
+    VideoPlayer: {
+        rateChange: 'Hızı ${rate}\'e değiştir',
+        qualityChange: 'Kaliteyi ${quality}\'e değiştir',
+        routeChange: 'Rota ${route}\'e değiştir',
+        mirror: 'Ayna',
+        cancelMirror: 'Aynayı kaldır',
+        loading: 'Yükleniyor...',
+        stall: 'Yükleme başarısız',
+        noResource: 'Kaynak yok',
+        videoError: 'Video yükleme hatası'
+    }
 };
 
 // [i18n-Turkish] 

+ 11 - 0
packages/semi-ui/locale/source/vi_VN.ts

@@ -198,6 +198,17 @@ const local: Locale = {
         replace: 'Thay thế',
         replaceAll: 'Thay thế tất cả',
     },
+    VideoPlayer: {
+        rateChange: 'Thay đổi tốc độ thành ${rate}',
+        qualityChange: 'Thay đổi chất lượng thành ${quality}',
+        routeChange: 'Thay đổi tuyến đường thành ${route}',
+        mirror: 'Gương',
+        cancelMirror: 'Hủy gương',
+        loading: 'Đang tải...',
+        stall: 'Tải không thành công',
+        noResource: 'Không có tài nguyên',
+        videoError: 'Lỗi tải video'
+    }
 };
 
 // [i18n-Vietnam] 越南语

+ 11 - 0
packages/semi-ui/locale/source/zh_CN.ts

@@ -196,6 +196,17 @@ const local: Locale = {
         replace: '替换',
         replaceAll: '全部替换',
     },
+    VideoPlayer: {
+        rateChange: '切换速率至 ${rate}',
+        qualityChange: '切换清晰度至${quality}',
+        routeChange: '切换线路至${route}',
+        mirror: '镜像',
+        cancelMirror: '取消镜像',
+        loading: '加载中...',
+        stall: '加载失败',
+        noResource: '暂无资源',
+        videoError: '视频加载错误'
+    }
 };
 
 // 中文

+ 11 - 0
packages/semi-ui/locale/source/zh_TW.ts

@@ -196,6 +196,17 @@ const local: Locale = {
         replace: '替換',
         replaceAll: '全部替換',
     },
+    VideoPlayer: {
+        rateChange: '切換速率至 ${rate}',
+        qualityChange: '切換清晰度至${quality}',
+        routeChange: '切換路徑至${route}',
+        mirror: '鏡像',
+        cancelMirror: '取消鏡像',
+        loading: '加載中...',
+        stall: '加載失敗',
+        noResource: '暫無資源',
+        videoError: '視頻加載錯誤'
+    }
 };
 
 // 中文

+ 7 - 7
packages/semi-ui/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-ui",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.",
     "main": "lib/cjs/index.js",
     "module": "lib/es/index.js",
@@ -20,12 +20,12 @@
         "@dnd-kit/core": "^6.0.8",
         "@dnd-kit/sortable": "^7.0.2",
         "@dnd-kit/utilities": "^3.2.1",
-        "@douyinfe/semi-animation": "2.79.0",
-        "@douyinfe/semi-animation-react": "2.79.0",
-        "@douyinfe/semi-foundation": "2.79.0",
-        "@douyinfe/semi-icons": "2.79.0",
-        "@douyinfe/semi-illustrations": "2.79.0",
-        "@douyinfe/semi-theme-default": "2.79.0",
+        "@douyinfe/semi-animation": "2.81.0",
+        "@douyinfe/semi-animation-react": "2.81.0",
+        "@douyinfe/semi-foundation": "2.81.0",
+        "@douyinfe/semi-icons": "2.81.0",
+        "@douyinfe/semi-illustrations": "2.81.0",
+        "@douyinfe/semi-theme-default": "2.81.0",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "copy-text-to-clipboard": "^2.1.1",

+ 19 - 0
packages/semi-ui/select/_story/select.stories.jsx

@@ -3720,3 +3720,22 @@ export const fix2465 = () => {
         </div>
     );
 }
+
+
+export const Fix2853 = () => {
+  return (
+    <Select placeholder="" style={{ width: 180 }} filter>
+        <Select.OptGroup key="a" label={<div>a</div>} >
+            <Select.Option value="a-1" key="a-1">a-1</Select.Option>
+            <Select.Option value="a-2" key="a-2">a-2</Select.Option>
+        </Select.OptGroup>
+        <Select.OptGroup label={<div>b</div>} >
+            <Select.Option value="b-1">b-1</Select.Option>
+            <Select.Option value="b-2">b-2</Select.Option>
+        </Select.OptGroup>
+        <Select.OptGroup label={<div>c</div>} >
+            <Select.Option value="c-1">c-1</Select.Option>
+        </Select.OptGroup>
+    </Select>
+  )
+}

+ 4 - 1
packages/semi-ui/select/index.tsx

@@ -908,8 +908,11 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             const parentGroup = option._parentGroup;
             const optionContent = this.renderOption(option, optionIndex);
             if (parentGroup && !groupStatus.has(parentGroup.label)) {
+                const groupKey = typeof parentGroup.label === 'string' || typeof parentGroup.label === 'number'
+                    ? parentGroup.label
+                    : parentGroup.key;
                 // when use with OptionGroup and group content not already insert
-                const groupContent = <OptionGroup {...parentGroup} key={parentGroup.label} />;
+                const groupContent = <OptionGroup {...parentGroup} key={groupKey}/>;
                 groupStatus.set(parentGroup.label, true);
                 content.push(groupContent);
             }

+ 1 - 0
packages/semi-ui/select/utils.tsx

@@ -55,6 +55,7 @@ const getOptionsFromGroup = (selectChildren: React.ReactNode) => {
             type = 'group';
             // Avoid saving children (reactNode) by... removing other props from the group except children, causing performance problems
             let { children, ...restGroupProps } = child.props;
+            restGroupProps.key = child.key;
             let originKeys = [];
             if (Array.isArray(children)) {
                 // if group has children > 1

+ 14 - 6
packages/semi-ui/tooltip/index.tsx

@@ -429,13 +429,21 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
             },
             canMotion: () => Boolean(this.props.motion),
             updateContainerPosition: () => {
-                const container = this.getPopupContainer();
-                if (container && isHTMLElement(container)) {
-                    // getComputedStyle need first parameter is Element type
-                    const computedStyle = window.getComputedStyle(container);
-                    const position = computedStyle.getPropertyValue('position');
-                    this.containerPosition = position;
+                const positionInBody = document.body.getAttribute('data-position');
+                if (positionInBody) {
+                    this.containerPosition = positionInBody;
+                    return;
                 }
+                requestAnimationFrame(() => {
+                    const container = this.getPopupContainer();
+                    if (container && isHTMLElement(container)) {
+                        // getComputedStyle need first parameter is Element type
+                        const computedStyle = window.getComputedStyle(container);
+                        const position = computedStyle.getPropertyValue('position');
+                        document.body.setAttribute('data-position', position);
+                        this.containerPosition = position;
+                    }
+                });
             },
             getContainerPosition: () => this.containerPosition,
             getContainer: () => this.containerEl && this.containerEl.current,

+ 1 - 1
packages/semi-ui/tsconfig.json

@@ -29,4 +29,4 @@
     },
     "include": ["**/*.tsx", "**/*.ts"],
     "exclude": ["node_modules", "packages/rollup-plugin-semi-svg"]
-}
+}

+ 15 - 0
packages/semi-ui/videoPlayer/ErrorSvg.tsx

@@ -0,0 +1,15 @@
+import React from 'react';
+
+const ErrorSvg = () => {
+    return <svg xmlns="http://www.w3.org/2000/svg" width="41" height="30" viewBox="0 0 41 30" fill="none">
+        <path 
+            fillRule="evenodd" 
+            clipRule="evenodd" 
+            d="M3.875 4.5C3.875 3.67157 4.54657 3 5.375 3H29.375C30.2034 3 30.875 3.67157 30.875 4.5V11.0446C30.875 11.9448 31.9714 12.3866 32.5956 11.7379L36.2647 7.92487C36.5768 7.60054 37.125 7.82146 37.125 8.27156V21.7284C37.125 22.1785 36.5768 22.3995 36.2647 22.0751L32.5956 18.2621C31.9714 17.6134 30.875 18.0552 30.875 18.9554V25.5C30.875 26.3284 30.2034 27 29.375 27H5.375C4.54657 27 3.875 26.3284 3.875 25.5V4.5ZM5.375 0C2.88972 0 0.875 2.01472 0.875 4.5V25.5C0.875 27.9853 2.88972 30 5.375 30H29.375C31.8603 30 33.875 27.9853 33.875 25.5V23.9183L34.103 24.1553C36.2876 26.4256 40.125 24.8792 40.125 21.7284V8.27156C40.125 5.12082 36.2876 3.5744 34.103 5.84475L33.875 6.08167V4.5C33.875 2.01472 31.8603 0 29.375 0H5.375ZM13.875 10.5C13.875 9.67157 13.2034 9 12.375 9C11.5466 9 10.875 9.67157 10.875 10.5V14.5C10.875 15.3284 11.5466 16 12.375 16C13.2034 16 13.875 15.3284 13.875 14.5V10.5ZM23.875 9C24.7034 9 25.375 9.67157 25.375 10.5V14.5C25.375 15.3284 24.7034 16 23.875 16C23.0466 16 22.375 15.3284 22.375 14.5V10.5C22.375 9.67157 23.0466 9 23.875 9ZM12.6231 22.3321C13.3039 21.3108 14.9319 20 18.375 20C20.192 20 21.3252 20.2374 22.1317 20.611C22.9124 20.9727 23.5161 21.5215 24.175 22.4C24.6721 23.0627 25.6123 23.1971 26.275 22.7C26.9377 22.2029 27.0721 21.2627 26.575 20.6C25.7339 19.4785 24.7706 18.5273 23.3928 17.889C22.0407 17.2626 20.424 17 18.375 17C14.0862 17 11.4461 18.6892 10.1269 20.6679C9.6674 21.3572 9.85366 22.2885 10.5429 22.7481C11.2322 23.2076 12.1635 23.0213 12.6231 22.3321Z" 
+            fill="#F9F9F9" 
+            fillOpacity="0.8"
+        />
+    </svg>;
+};
+
+export default ErrorSvg;

+ 271 - 0
packages/semi-ui/videoPlayer/_story/videoPlayer.stories.jsx

@@ -0,0 +1,271 @@
+import React, { useState } from 'react';
+import { VideoPlayer, Select, Typography } from '@douyinfe/semi-ui';
+
+export default {
+    title: 'VideoPlayer',
+};
+
+export const BasicUsage = () => {
+    return (
+        <div>
+            <VideoPlayer 
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                width={'500px'}
+                height={'280px'} 
+                onPause={() => {
+                    console.log('pause');
+                }}
+                onPlay={() => {
+                    console.log('play');
+                }}
+            />
+        </div>
+    )
+}
+
+// 设置菜单栏功能
+export const ControlList = () => {
+    return (
+        <div>
+            <VideoPlayer 
+                width={'500px'}
+                height={'280px'} 
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                controlsList={['play', 'time', 'volume', 'playbackRate', 'fullscreen',]}
+            />
+        </div>
+    )
+}
+
+export const Theme = () => {
+    return (
+        <div>
+            <Typography.Title heading={3} style={{ margin: '8px 0' }} >dark theme</Typography.Title>
+            <VideoPlayer 
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                width={'1000px'}
+                height={'450px'} 
+                controlsList={['play', 'time', 'volume', 'playbackRate', 'fullscreen',]}
+            />
+            <br />
+            <Typography.Title heading={3} style={{ margin: '8px 0' }} >light theme</Typography.Title>
+            <VideoPlayer 
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                width={'1000px'}
+                height={'450px'} 
+                theme={'light'}
+                controlsList={['play', 'time', 'volume', 'playbackRate', 'fullscreen',]}
+            />
+        </div>
+    )
+}
+
+export const SetSeekTime = () => {
+    const [seekTime, setSeekTime] = useState(5);
+    return (
+        <div>
+            <span style={{ marginBottom: 10 }}>请选择快进快退时间</span>
+            <Select
+                value={seekTime}
+                style={{ width: 100, marginLeft: 10 }}
+                onChange={(value) => setSeekTime(value)}
+                optionList={[
+                    { label: '5s', value: 5 },
+                    { label: '10s', value: 10 },
+                    { label: '15s', value: 15 },
+                ]}
+                placeholder='请选择快进快退时间'
+            />
+            <br />
+            <VideoPlayer 
+                width={'500px'}
+                height={'280px'} 
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                seekTime={seekTime}
+                style={{ marginTop: 10 }}
+            />
+        </div>
+    )
+}
+
+export const playbackRateList = () => {
+    return (
+        <div>
+            <VideoPlayer 
+                width={'500px'}
+                height={'280px'} 
+                playbackRateList={[
+                    { label: '0.5x', value: 0.5 },
+                    { label: '1.0x', value: 1 },
+                    { label: '1.5x', value: 1.5 },
+                    { label: '2.0x', value: 2 },
+                ]}
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+            />
+        </div>
+    )
+}
+
+// 音量设置
+export const Volume = () => {
+    const src = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4";
+    const poster = "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg";
+    return (
+        <VideoPlayer 
+            width={'500px'}
+            height={'280px'} 
+            src={src}
+            poster={poster}
+            muted={true}
+            volume={0}
+        />
+    );
+}
+
+export const NoResource = () => {
+    return (
+        <div>
+            <Typography.Title heading={3} style={{ margin: '8px 0' }} >dark theme</Typography.Title>
+            <VideoPlayer 
+                width={'500px'}
+                height={'280px'} 
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-videoss.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+            />
+            <br />
+            <Typography.Title heading={3} style={{ margin: '8px 0' }} >light theme</Typography.Title>
+            <VideoPlayer 
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-videoss.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                theme={'light'}
+                width={'500px'}
+                height={'280px'} 
+            />
+        </div>
+    )
+}
+
+
+// 分章节
+export const Chapter = () => {
+    return (
+        <div>
+            <VideoPlayer 
+                width={'800px'}
+                height={'450px'} 
+                src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4'}
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                markers={[
+                    {
+                        start: 0,
+                        title: '片头'
+                    },
+                    {
+                        start: 4,
+                        title: '功能介绍'
+                    },
+                    {
+                        start: 38,
+                        title: 'Figma Plugin'
+                    },
+                    {
+                        start: 51,
+                        title: '片尾'
+                    }
+                ]}
+            />
+        </div>
+    )
+}
+
+// 清晰度和线路调整
+export const QualityAndLine = () => {
+    const [quality, setQuality] = useState('1080p');
+    const [route, setRoute] = useState('线路1');
+    const [src, setSrc] = useState('https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4');
+
+    const playList = [
+        {
+            src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4',
+            quality: '1080p',
+            route: '线路1',
+        },
+        {
+            src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/video/vchart-show-video-480p.mp4',
+            quality: '480p',
+            route: '线路1',
+        },
+        {
+            src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4',
+            quality: '1080p',
+            route: '线路2',
+        },
+        {
+            src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/video/vchart-show-video-480p.mp4',
+            quality: '480p',
+            route: '线路2',
+        }
+    ]
+
+    const updateVideoSource = (quality, route) => {
+        const source = playList.find((item) => item.quality === quality && item.route === route);
+        console.log('updateVideoSource', quality, route, source);
+        setSrc(source.src);
+    }
+
+    return (
+        <div>
+            <VideoPlayer 
+                src={src}
+                width={'800px'}
+                height={'450px'} 
+                poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                defaultQuality={'1080p'}
+                defaultRoute={'线路1'}
+                qualityList={[
+                    { label: '1080p', value: '1080p' },
+                    { label: '480p', value: '480p' },
+                ]}
+                routeList={[
+                    { label: '线路1', value: '线路1' },
+                    { label: '线路2', value: '线路2' },
+                ]}
+                onQualityChange={(quality) => {
+                    console.log('quality change', quality);
+                    updateVideoSource(quality, route);
+                    setQuality(quality);
+                }}
+                onRouteChange={(route) => {
+                    console.log('route change', route);
+                    updateVideoSource(quality, route);
+                    setRoute(route);
+                }}
+            />
+        </div>
+    )
+}
+
+export const ScrollDemo = () => {
+    return (
+        <div>
+            滑动到底部全屏后,取消全屏,check 页面滚动是否符合预期
+            <div style={{ marginTop: 1000, overflow: 'auto' }}>
+                <VideoPlayer 
+                    src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/vchart/landingPage/vchart-show-video.mp4'}
+                    poster={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/poster2.jpeg'}
+                    theme={'light'}
+                    width={500}
+                    height={280}
+                />
+            </div>
+        </div>
+    )
+}
+
+

+ 519 - 0
packages/semi-ui/videoPlayer/index.tsx

@@ -0,0 +1,519 @@
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+import React from 'react';
+import cls from 'classnames';
+import BaseComponent from '../_base/baseComponent';
+import { cssClasses, DEFAULT_PLAYBACK_RATE, numbers, strings } from '@douyinfe/semi-foundation/videoPlayer/constants';
+import VideoPlayerFoundation, { VideoPlayerAdapter } from '@douyinfe/semi-foundation/videoPlayer/foundation';
+import '@douyinfe/semi-foundation/videoPlayer/videoPlayer.scss';
+import { IconPlay, IconPause, IconVolume1, IconVolume2, IconRestart, IconFlipHorizontal, IconMinimize, IconMaximize, IconMute, IconPlayCircle, IconMiniPlayer } from '@douyinfe/semi-icons';
+import Button from '../button';
+import Popover from '../popover';
+import AudioSlider from '../audioPlayer/audioSlider';
+import Dropdown from '../dropdown';
+import VideoProgress from './videoProgress';
+import { formatTime } from './utils';
+import isNullOrUndefined from '@douyinfe/semi-foundation/utils/isNullOrUndefined';
+import LocaleConsumer from '../locale/localeConsumer';
+import { Locale } from '../locale/interface';
+import ErrorSVG from './ErrorSvg';
+import { Marker } from '@douyinfe/semi-foundation/videoPlayer/progressFoundation';
+
+const prefixCls = cssClasses.PREFIX;
+
+export interface VideoPlayerProps {
+    autoPlay: boolean;
+    captionsSrc?: string;
+    className?: string;
+    clickToPlay: boolean;
+    controlsList?: Array<string>;
+    crossOrigin?: React.MediaHTMLAttributes<HTMLVideoElement>['crossOrigin'];
+    defaultPlaybackRate: number;
+    defaultQuality?: string;
+    defaultRoute?: string;
+    height?: number | string;
+    loop?: boolean;
+    markers?: Marker[];
+    muted: boolean;
+    onPause?: () => void;
+    onPlay?: () => void;
+    onQualityChange?: (quality: string) => void;
+    onRateChange?: (rate: number) => void;
+    onRouteChange?: (route: string) => void;
+    onVolumeChange?: (volume: number) => void;
+    playbackRateList: { label: string; value: number }[];
+    poster?: string;
+    // todo: 预览缩略图
+    // previewThumbnails?: boolean | Record<string, unknown>;
+    qualityList?: Array<{ label: string; value: string }>;
+    routeList?: Array<{ label: string; value: string }>;
+    seekTime?: number;
+    src?: string;
+    style?: React.CSSProperties;
+    theme: string;
+    volume: number;
+    width?: number | string
+}
+
+export interface VideoPlayerState {
+    bufferedValue: number;
+    currentQuality: string;
+    currentRoute: string;
+    currentTime: number;
+    isError: boolean;
+    isMirror: boolean;
+    isPlaying: boolean;
+    muted: boolean;
+    notificationContent: string;
+    playbackRate: number;
+    playbackRateList: { label: string; value: number }[];
+    showNotification: boolean;
+    showControls: boolean;
+    src: string;
+    totalTime: number;
+    volume: number
+}
+
+class VideoPlayer extends BaseComponent<VideoPlayerProps, VideoPlayerState> {
+    static defaultProps: VideoPlayerProps = {
+        autoPlay: false,
+        clickToPlay: true,
+        defaultPlaybackRate: numbers.DEFAULT_PLAYBACK_RATE,
+        controlsList: [strings.PLAY, strings.NEXT, strings.TIME, strings.VOLUME, strings.PLAYBACK_RATE, strings.QUALITY, strings.ROUTE, strings.MIRROR, strings.FULLSCREEN, strings.PICTURE_IN_PICTURE],
+        loop: false,
+        muted: false,
+        playbackRateList: DEFAULT_PLAYBACK_RATE,
+        seekTime: numbers.DEFAULT_SEEK_TIME,
+        theme: strings.DARK,
+        volume: numbers.DEFAULT_VOLUME,
+    };
+
+    private videoRef: React.RefObject<HTMLVideoElement>;
+    private videoWrapperRef: React.RefObject<HTMLDivElement>;
+    foundation: VideoPlayerFoundation;
+
+    constructor(props: VideoPlayerProps) {
+        super(props);
+        this.state = {
+            bufferedValue: 0,
+            currentQuality: props.defaultQuality || '',
+            currentRoute: props.defaultRoute || '',
+            currentTime: 0,
+            isError: false,
+            isMirror: false,
+            isPlaying: false,
+            muted: props.muted,
+            notificationContent: '',
+            playbackRate: props.defaultPlaybackRate || 1,
+            playbackRateList: props.playbackRateList,
+            showNotification: false,
+            showControls: true,
+            src: props.src || '',
+            totalTime: 0,
+            volume: props.muted ? 0 : props.volume,
+        };
+        this.videoRef = React.createRef();
+        this.videoWrapperRef = React.createRef();
+        this.foundation = new VideoPlayerFoundation(this.adapter);
+    }
+
+    get adapter(): VideoPlayerAdapter<VideoPlayerProps, VideoPlayerState> {
+        return {
+            ...super.adapter,
+            getVideo: () => this.videoRef.current,
+            getVideoWrapper: () => this.videoWrapperRef.current,
+            notifyPause: () => this.props.onPause?.(),
+            notifyPlay: () => this.props.onPlay?.(),
+            notifyQualityChange: (quality: string) => this.props.onQualityChange?.(quality),
+            notifyRateChange: (rate: number) => this.props.onRateChange?.(rate),
+            notifyRouteChange: (route: string) => this.props.onRouteChange?.(route),
+            notifyVolumeChange: (volume: number) => this.props.onVolumeChange?.(volume),
+            setBufferedValue: (bufferedValue: number) => this.setState({ bufferedValue }),
+            setCurrentTime: (currentTime: number) => this.setState({ currentTime }),
+            setIsError: (isError: boolean) => this.setState({ isError }),
+            setIsMirror: (isMirror: boolean) => this.setState({ isMirror }),
+            setIsPlaying: (isPlaying: boolean) => this.setState({ isPlaying }),
+            setMuted: (muted: boolean) => this.setState({ muted }),
+            setNotificationContent: (content: string) => this.setState({ notificationContent: content }),
+            setPlaybackRate: (rate: number) => this.setState({ playbackRate: rate }),
+            setQuality: (quality: string) => this.setState({ currentQuality: quality }),
+            setRoute: (route: string) => this.setState({ currentRoute: route }),
+            setShowControls: (showControls: boolean) => this.setState({ showControls }),
+            setShowNotification: (showNotification: boolean) => this.setState({ showNotification: showNotification }),
+            setTotalTime: (totalTime: number) => this.setState({ totalTime }),
+            setVolume: (volume: number) => this.setState({ volume }),
+        };
+    }
+
+    static getDerivedStateFromProps(props: VideoPlayerProps, state: VideoPlayerState): Partial<VideoPlayerState> {
+        const states: Partial<VideoPlayerState> = {};
+        if (!isNullOrUndefined(props.src) && props.src !== state.src) {
+            states.src = props.src;
+        }
+        return states;
+    }
+
+    componentDidMount() {
+        this.foundation.init();
+    }
+
+    componentWillUnmount() {
+        this.foundation.destroy();
+    }
+
+    handleMouseEnterWrapper = () => {
+        this.foundation.handleMouseEnterWrapper();
+    }
+
+    handleMouseLeaveWrapper = () => {
+        this.foundation.handleMouseLeaveWrapper();
+    }
+
+    handleTimeChange = (value: number) => {
+        this.foundation.handleTimeChange(value);
+    }
+
+    handleTimeUpdate = () => {
+        this.foundation.handleTimeUpdate();
+    }
+
+    handleError = () => {
+        this.foundation.handleError();
+    }
+
+    handlePlay = () => {
+        this.foundation.handlePlay();
+    }
+
+    handlePause = () => {
+        this.foundation.handlePause();
+    }
+
+    handleCanPlay = () => {
+        this.foundation.handleCanPlay();
+    }
+
+    handleWaiting = (locale: Locale['VideoPlayer']) => {
+        this.foundation.handleWaiting(locale);
+    }
+
+    handleStalled = (locale: Locale['VideoPlayer']) => {
+        this.foundation.handleStalled(locale);
+    }
+
+    handleProgress = () => {
+        this.foundation.handleProgress();
+    }
+
+    handleEnded = () => {
+        this.foundation.handleEnded();
+    }
+
+    handleDurationChange = () => {
+        this.foundation.handleDurationChange();
+    }
+
+    handleVolumeChange = (value: number) => {
+        this.foundation.handleVolumeChange(value);
+    }
+
+    handleVolumeSilent = () => {
+        this.foundation.handleVolumeSilent();
+    }
+
+    handleRateChange = (option: { label: string; value: number }, locale: Locale['VideoPlayer']) => {
+        this.foundation.handleRateChange(option, locale);
+    }
+
+    handleQualityChange = (option: { label: string; value: string }, locale: Locale['VideoPlayer']) => {
+        this.foundation.handleQualityChange(option, locale);
+    }
+
+    handleRouteChange = (option: { label: string; value: string }, locale: Locale['VideoPlayer']) => {
+        this.foundation.handleRouteChange(option, locale);
+    }
+    
+    handleMirror = (locale: Locale['VideoPlayer']) => {
+        this.foundation.handleMirror(locale);
+    }
+
+    handleFullscreen = () => {
+        this.foundation.handleFullscreen();
+    }
+
+    handlePictureInPicture = () => {
+        this.foundation.handlePictureInPicture();
+    }
+
+    getVolumeIcon = () => {
+        const { volume, muted } = this.state;
+        if (muted) {
+            return <IconMute />;
+        }
+        if (volume < 50) {
+            return <IconVolume1 />;
+        }
+        return <IconVolume2 />;
+    }
+
+    isResourceNotFound = () => {
+        const { src } = this.props;
+        return isNullOrUndefined(src);
+    }
+
+    renderTime = () => {
+        const { currentTime, totalTime } = this.state;
+        if (this.foundation.shouldShowControlItem(strings.TIME)) {
+            return <div className={cls(`${cssClasses.PREFIX_CONTROLS}-time`)}>
+                {formatTime(currentTime)} / {formatTime(totalTime)}
+            </div>;
+        }
+        return null;
+    }
+
+    renderResourceNotFound = (locale: Locale['VideoPlayer']) => {
+        return (
+            <div className={cls(`${prefixCls}-resource-not-found`)}>
+                {locale.noResource}
+            </div>
+        );
+    }
+
+    renderPauseIcon = () => {
+        const { isPlaying, isError } = this.state;
+        if (!isPlaying && !isError) {
+            return (
+                // eslint-disable-next-line jsx-a11y/no-static-element-interactions
+                <div className={cls(`${prefixCls}-pause`)} >
+                    <IconPlayCircle />
+                </div>
+            );
+        }
+        return null;
+    }
+
+    renderError = (locale: Locale['VideoPlayer']) => {
+        const { isError } = this.state;
+        const { theme } = this.props;
+        if (isError) {
+            return (
+                <div className={cls(`${prefixCls}-error`, 
+                    { [`${prefixCls}-error-${theme}`]: theme })}
+                >
+                    <div className={cls(`${prefixCls}-error-svg`)}>
+                        <ErrorSVG />
+                    </div>
+                    {locale.videoError}
+                </div>
+            );
+        }
+        return null;
+    }
+
+    renderPoster = () => {
+        const { poster } = this.props;
+        const { isPlaying, currentTime, totalTime } = this.state;
+        const isHide = currentTime > 0 && currentTime < totalTime;
+        if (!isPlaying && poster) {
+            return (
+                <img 
+                    className={cls(`${prefixCls}-poster`, 
+                        { [`${prefixCls}-poster-hide`]: isHide }
+                    )}
+                    src={poster} 
+                    alt="poster" 
+                />
+            );
+        }
+        return null;
+    }
+
+    renderNotification = () => {
+        const { showNotification, notificationContent } = this.state;
+
+        if (!showNotification || !notificationContent) {
+            return null;
+        }
+
+        return (
+            <div className={cls(`${prefixCls}-notification`)}>
+                {this.state.notificationContent}
+            </div>
+        );
+    }
+
+    renderVolume = () => {
+        const { volume, muted } = this.state;
+        if (this.foundation.shouldShowControlItem(strings.VOLUME)) {
+            return (
+                <Popover 
+                    autoAdjustOverflow
+                    position='top'
+                    className={cls(`${cssClasses.PREFIX_CONTROLS}-popover`)}
+                    content={
+                        <div className={cls(`${cssClasses.PREFIX_CONTROLS}-volume`)}>
+                            <div className={cls(`${cssClasses.PREFIX_CONTROLS}-volume-title`)}>{muted ? 0 : volume}%</div>
+                            <AudioSlider 
+                                value={muted ? 0 : volume} 
+                                max={100} 
+                                vertical 
+                                height={120} 
+                                showTooltip={false} 
+                                onChange={this.handleVolumeChange} 
+                            />
+                        </div>
+                    }
+                >
+                    <Button
+                        className={cls(
+                            `${cssClasses.PREFIX_CONTROLS}-menu-item`, 
+                            `${cssClasses.PREFIX_CONTROLS}-menu-button`)
+                        }
+                        theme={'borderless'}
+                        icon={this.getVolumeIcon()}
+                        onClick={this.handleVolumeSilent}
+                    />
+                </Popover>
+            );
+        }
+        return null;
+    }
+
+    renderIconButton = (icon: React.ReactNode, onClick: () => void, name: string) => {
+        if (!this.foundation.shouldShowControlItem(name)) {
+            return null;
+        }
+        return (
+            <Button
+                theme={'borderless'}
+                className={cls(
+                    `${cssClasses.PREFIX_CONTROLS}-menu-item`, 
+                    `${cssClasses.PREFIX_CONTROLS}-menu-button`
+                )}
+                icon={icon}
+                onClick={onClick}
+            />
+        );
+    }
+
+    renderDropdownButton = (currentValue: string | number, list: { label: string; value: number | string }[], handleChange: (option: { label: string; value: any }, locale: Locale['VideoPlayer']) => void, name: string, locale: Locale['VideoPlayer']) => {
+        if (this.foundation.shouldShowControlItem(name)) {
+            return (
+                <Dropdown 
+                    position='top'
+                    className={cls(`${cssClasses.PREFIX_CONTROLS}-popup-menu`)} 
+                    render={
+                        <Dropdown.Menu>
+                            {list.map((option) => (
+                                <Dropdown.Item 
+                                    className={cls(`${cssClasses.PREFIX_CONTROLS}-popup-menu-item`)} 
+                                    key={option.value} 
+                                    onClick={() => handleChange(option, locale)} 
+                                    active={option.value === currentValue}
+                                >
+                                    {option.label}
+                                </Dropdown.Item>
+                            ))}
+                        </Dropdown.Menu>
+                    } 
+                    onChange={(option: { label: string; value: any }) => handleChange(option, locale)}
+                >
+                    <div 
+                        className={cls(
+                            `${cssClasses.PREFIX_CONTROLS}-menu-item`, 
+                            `${cssClasses.PREFIX_CONTROLS}-popup`)
+                        }
+                    >
+                        {list.find((option) => option.value === currentValue)?.label}
+                    </div>
+                </Dropdown>
+            );
+        }
+        return null;
+    }
+
+    render() {
+        const { markers, qualityList, routeList, width, height, autoPlay, style, className, loop, captionsSrc, crossOrigin, theme } = this.props;
+        const { isPlaying, playbackRate, playbackRateList, isMirror, currentTime, totalTime, currentQuality, currentRoute, src, bufferedValue, showControls } = this.state;
+
+        return (
+            <LocaleConsumer componentName="VideoPlayer">
+                {(locale: Locale['VideoPlayer']) => { 
+                    return (
+                        <div 
+                            className={cls(`${prefixCls}`,
+                                className,
+                                { [`${prefixCls}-mirror`]: isMirror },
+                            )}
+                            style={{ width, height, ...style }}
+                            ref={this.videoWrapperRef}
+                            onMouseEnter={this.handleMouseEnterWrapper}
+                            onMouseLeave={this.handleMouseLeaveWrapper} 
+                        >
+                            <div className={cls(`${prefixCls}-wrapper`,
+                                { [`${cssClasses.PREFIX}-wrapper-${theme}`]: theme }
+                            )}>
+                                <video 
+                                    ref={this.videoRef} 
+                                    autoPlay={autoPlay}
+                                    loop={loop}
+                                    controls={false}
+                                    crossOrigin={crossOrigin}
+                                    src={src}
+                                    onTimeUpdate={this.handleTimeUpdate}
+                                    onDurationChange={this.handleDurationChange}
+                                    onClick={() => { this.foundation.handlePlayOrPause();}}
+                                    // An error occurred while getting the media data, or the resource is in an unsupported format.
+                                    onError={this.handleError}
+                                    onCanPlay={this.handleCanPlay}
+                                    // Playback stopped due to temporary lack of data.
+                                    onWaiting={() => this.handleWaiting(locale)}
+                                    // The user agent attempted to fetch media data but was unexpectedly unable to fetch the data.
+                                    onStalled={() => this.handleStalled(locale)}
+                                    onProgress={this.handleProgress}
+                                    onEnded={this.handleEnded}
+                                >
+                                    <track kind="captions" src={captionsSrc}/>
+                                </video>
+                                {this.isResourceNotFound() && this.renderResourceNotFound(locale)}
+                            </div>
+                            {this.renderPoster()}
+                            {this.renderPauseIcon()}
+                            {this.renderError(locale)}
+                            {this.renderNotification()}
+                            <div className={cls(`${cssClasses.PREFIX_CONTROLS}`,
+                                { [`${cssClasses.PREFIX_CONTROLS}-hide`]: !showControls }
+                            )}>
+                                <VideoProgress 
+                                    key={totalTime}
+                                    value={currentTime} 
+                                    max={totalTime} 
+                                    onChange={this.handleTimeChange}
+                                    markers={markers}
+                                    bufferedValue={bufferedValue}
+                                />
+                                <div className={cls(`${cssClasses.PREFIX_CONTROLS}-menu`)}>
+                                    <div className={cls(`${cssClasses.PREFIX_CONTROLS}-menu-left`)}>
+                                        {this.renderIconButton(isPlaying ? <IconPause /> : <IconPlay />, isPlaying ? this.handlePause : this.handlePlay, strings.PLAY)}
+                                        {this.renderIconButton(<IconRestart rotate={180} />, isPlaying ? this.handlePause : this.handlePlay, strings.NEXT)}
+                                        {this.renderTime()}
+                                        {this.renderVolume()}
+                                        {this.renderDropdownButton(playbackRate, playbackRateList, this.handleRateChange, strings.PLAYBACK_RATE, locale)}
+                                    </div>
+                                    <div className={cls(`${cssClasses.PREFIX_CONTROLS}-menu-right`)}>
+                                        {qualityList && qualityList.length > 0 && this.renderDropdownButton(currentQuality, qualityList, this.handleQualityChange, strings.QUALITY, locale)}
+                                        {routeList && routeList.length > 0 && this.renderDropdownButton(currentRoute, routeList, this.handleRouteChange, strings.ROUTE, locale)}
+                                        {this.renderIconButton(<IconFlipHorizontal />, () => this.handleMirror(locale), strings.MIRROR)}
+                                        {this.renderIconButton(this.foundation.checkFullScreen() ? <IconMinimize /> : <IconMaximize />, this.handleFullscreen, strings.FULLSCREEN)}
+                                        {this.renderIconButton(<IconMiniPlayer />, this.handlePictureInPicture, strings.PICTURE_IN_PICTURE)}
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    );
+                }}
+            </LocaleConsumer>
+        );
+    }
+}
+
+export default VideoPlayer; 

+ 15 - 0
packages/semi-ui/videoPlayer/utils.ts

@@ -0,0 +1,15 @@
+export const formatTime = (time: number) => {
+    if (isNaN(time)) {
+        return '00:00';
+    }
+    const hours = Math.floor(time / 3600);
+    if (hours > 0) {
+        const minutes = Math.floor((time - hours * 3600) / 60);
+        const seconds = Math.floor(time % 60);
+        return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+    } else {
+        const minutes = Math.floor(time / 60);
+        const seconds = Math.floor(time % 60);
+        return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+    }
+};

+ 193 - 0
packages/semi-ui/videoPlayer/videoProgress.tsx

@@ -0,0 +1,193 @@
+import React from 'react';
+import cls from 'classnames';
+import '@douyinfe/semi-foundation/videoPlayer/videoPlayer.scss';
+import { cssClasses } from '@douyinfe/semi-foundation/videoPlayer/constants';
+import VideoProgressFoundation, { Marker, MarkerListItem, VideoProgressAdapter } from '@douyinfe/semi-foundation/videoPlayer/progressFoundation';
+import Tooltip from '../tooltip';
+import { noop } from 'lodash';
+import { formatTime } from './utils';
+import BaseComponent from '../_base/baseComponent';
+
+export interface VideoProgressProps {
+    value: number;
+    onChange: (value: number) => void;
+    className?: string;
+    max: number;
+    showTooltip?: boolean;
+    markers?: Marker[];
+    bufferedValue: number
+}
+
+export interface VideoProgressState {
+    isDragging: boolean;
+    isHandleHovering: boolean;
+    movingInfo: { progress: number; offset: number; value: number } | null;
+    activeIndex: number
+}
+
+export default class VideoProgress extends BaseComponent<VideoProgressProps, VideoProgressState> {
+    static defaultProps = {
+        value: 0,
+        onChange: noop,
+        max: 100,
+        showTooltip: true,
+    };
+
+    private sliderRef: React.RefObject<HTMLDivElement>;
+    private handleRef: React.RefObject<HTMLDivElement>;
+    private markersList: MarkerListItem[];
+    foundation: VideoProgressFoundation;
+
+    constructor(props: VideoProgressProps) {
+        super(props);
+        this.state = {
+            isDragging: false,
+            isHandleHovering: false,
+            movingInfo: null,
+            activeIndex: -1, // Used to determine which slider the current handle is on under the dragging state
+        };
+
+        this.sliderRef = React.createRef();
+        this.handleRef = React.createRef();
+        this.markersList = this.initMarkerList();
+        this.foundation = new VideoProgressFoundation(this.adapter);
+    }
+
+    get adapter(): VideoProgressAdapter<VideoProgressProps, VideoProgressState> {
+        return {
+            ...super.adapter,
+            getSliderRef: () => this.sliderRef.current,
+            getMarkersList: () => this.markersList,
+            setIsDragging: (isDragging: boolean) => this.setState({ isDragging }),
+            setIsHandleHovering: (isHandleHovering: boolean) => this.setState({ isHandleHovering }),
+            setActiveIndex: (activeIndex: number) => this.setState({ activeIndex }),
+            setMovingInfo: (movingInfo: { progress: number; offset: number; value: number } | null) => this.setState({ movingInfo }),
+        };
+    }
+
+    initMarkerList = () => {
+        const { markers, max } = this.props;
+        const hasMarkers = markers && markers.length > 0;
+        const defaultMarker: MarkerListItem = {
+            start: 0,
+            end: max,
+            left: '0',
+            title: '',
+            width: '100%',
+        };
+        const newMarkers = hasMarkers ? [...markers] : [defaultMarker];
+        let markersList: MarkerListItem[] = [];
+        if (hasMarkers) {
+            newMarkers.forEach((marker: MarkerListItem | Marker, index: number) => {
+                const end = index === newMarkers.length - 1 ? max : newMarkers[index + 1].start;
+                if (!(marker.start > max || end > max)) {
+                    const item = {
+                        left: `${(marker.start / max) * 100}%`,
+                        width: `${max ? (end - marker.start) / max * 100 : 100}%`,
+                        end: end,
+                        start: marker.start,
+                        title: marker.title
+                    };
+                    markersList.push(item);
+                }
+            });
+        } else {
+            markersList.push(defaultMarker);
+        }
+        return markersList;
+    }
+
+    handleMouseEnter = (e: any) => {
+        this.foundation.handleMouseEvent(e, false);
+    }
+
+    handleMouseMove = (e: any) => {
+        this.foundation.handleMouseEvent(e, true);
+    }
+
+    renderTooltipContent = () => {
+        const { movingInfo } = this.state;
+        if (this.markersList.length > 0 && movingInfo) {
+            const hoverIndex = this.markersList.findIndex((marker: MarkerListItem) => {
+                return movingInfo?.value > marker.start && movingInfo?.value < marker.end;
+            });
+            return (
+                <>
+                    <div className={cls(`${cssClasses.PREFIX_PROGRESS}-tooltip-content`)}>
+                        {this.markersList[hoverIndex]?.title}
+                    </div>
+                    <div className={cls(`${cssClasses.PREFIX_PROGRESS}-tooltip-content`)}>
+                        {formatTime(movingInfo.progress * this.props.max)}
+                    </div>
+                </>
+            );
+        }
+        return movingInfo && formatTime(movingInfo.progress * this.props.max);
+    }
+
+    render() {
+        const { showTooltip, max, value: currentValue } = this.props;
+        const { movingInfo, isHandleHovering, isDragging, activeIndex } = this.state;
+        const sliderContent = (
+            <div
+                role="slider"
+                tabIndex={0}
+                aria-valuenow={currentValue as number}
+                ref={this.sliderRef}
+                className={cls(`${cssClasses.PREFIX_PROGRESS}`)}
+                onMouseDown={this.foundation.handleMouseDown}
+                onMouseUp={this.foundation.handleMouseUp}
+                onMouseEnter={this.handleMouseEnter}
+                onMouseMove={this.handleMouseMove}
+            >
+                <div className={cls(`${cssClasses.PREFIX_PROGRESS}-markers`)}>
+                    {
+                        this.markersList.map((marker: MarkerListItem, index: number) => (
+                            <div
+                                key={`${marker.start}-${index}`}   
+                                className={cls(`${cssClasses.PREFIX_PROGRESS}-slider`,
+                                    { [`${cssClasses.PREFIX_PROGRESS}-slider-active`]: index === activeIndex && isDragging }
+                                )}
+                                style={{ left: marker.left, width: marker.width }}
+                                onMouseEnter={() => this.foundation.handleSliderMouseEnter(index)}
+                                onMouseLeave={() => this.foundation.handleSliderMouseLeave(index)}
+                            >
+                                <div className={cls(`${cssClasses.PREFIX_PROGRESS}-slider-list`)} />
+                                <div
+                                    className={cls(`${cssClasses.PREFIX_PROGRESS}-slider-buffered`)}
+                                    style={{ width: this.foundation.getLoadedWidth(marker) }}
+                                />
+                                <div
+                                    className={cls(`${cssClasses.PREFIX_PROGRESS}-slider-played`)}
+                                    style={{ width: this.foundation.getPlayedWidth(marker) }}
+                                />
+                            </div>
+                        ))
+                    }
+                </div>
+                <div
+                    ref={this.handleRef}
+                    className={cls(`${cssClasses.PREFIX_PROGRESS}-handle`)}
+                    style={{
+                        left: `calc(${((max ? (currentValue || 1) / max : 0) * 100)}% - 8px)`,
+                        transform: 'translateY(-50%)',
+                        opacity: (isHandleHovering || isDragging) ? 1 : 0,
+                        transition: 'opacity 0.3s',
+                        pointerEvents: 'none',
+                    }}
+                />
+            </div>
+        );
+
+        return showTooltip ? (
+            <Tooltip
+                position={'top'}
+                className={cls(`${cssClasses.PREFIX_PROGRESS}-tooltip`)}
+                content={this.renderTooltipContent()}
+                style={{ 'left': movingInfo?.offset }}
+            >
+                {sliderContent}
+            </Tooltip>
+        ) : sliderContent;
+    }
+}

+ 1 - 1
packages/semi-webpack/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-webpack-plugin",
-    "version": "2.79.0",
+    "version": "2.81.0",
     "description": "",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",

File diff suppressed because it is too large
+ 216 - 235
sitemap.xml


+ 3 - 0
src/images/docIcons/doc-videoplayer.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1 6C1 4.89543 1.89543 4 3 4H14C15.1046 4 16 4.89543 16 6V18C16 19.1046 15.1046 20 14 20H3C1.89543 20 1 19.1046 1 18V6ZM4.9823 8H7.9823V11H4.9823V8ZM23.0001 7.6185C23.0001 6.875 22.2176 6.39151 21.5526 6.72418L17.5565 8.72354C17.2178 8.89298 17.0039 9.23918 17.0039 9.61786V14.3821C17.0039 14.7608 17.2178 15.107 17.5565 15.2765L21.5526 17.2758C22.2176 17.6085 23.0001 17.125 23.0001 16.3815V7.6185Z" fill="#AAB2BF"/>
+</svg>

+ 76 - 51
yarn.lock

@@ -1592,13 +1592,13 @@
     "@douyinfe/semi-animation-styled" "2.65.0"
     classnames "^2.2.6"
 
-"@douyinfe/semi-animation-react@2.78.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.78.0.tgz#25db969d363b649052c809fc9a7f1a721a51c915"
-  integrity sha512-81DUulQUnjjuWeAw73gOay+BHLEHchCcvGhyWZVL1s/CYi73AoivmraWc5GrCWdZzT8nMxSAJ85hesXnwGVHgg==
+"@douyinfe/[email protected]0.0":
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.80.0.tgz#b6fa136f6fe61c3afe0c56b440891638701fa3c7"
+  integrity sha512-DL4nbJOOgiR/xVYQITqzXzYDoW85AhJ5ZwxXWQ/r5hwmuWzhCOg5RbpcCEfj6EM+TOYCkopihrwxC5x2hu+LMw==
   dependencies:
-    "@douyinfe/semi-animation" "2.78.0"
-    "@douyinfe/semi-animation-styled" "2.78.0"
+    "@douyinfe/semi-animation" "2.80.0"
+    "@douyinfe/semi-animation-styled" "2.80.0"
     classnames "^2.2.6"
 
 "@douyinfe/[email protected]":
@@ -1606,10 +1606,10 @@
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.65.0.tgz#8c56047a5704a45b05cc9809a2a126cc24526ea1"
   integrity sha512-YFF8Ptcz/jwS0phm28XZV7ROqMQ233sjVR0Uy33FImCITr6EAPe5wcCeEmzVZoYS7x3tUFR30SF+0hSO01rQUg==
 
-"@douyinfe/semi-animation-styled@2.78.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.78.0.tgz#e3d9fd66625bd3d46105fc72fe6e0c2ca47775dc"
-  integrity sha512-NYMuI4SOl4+fV4/S8qJfL8ip1EkjO+G6rtlWTmtvc77ENHoBsf6C+qLxupi5e+IKGBy/Vfr52hPDOsOC8FWokQ==
+"@douyinfe/[email protected]0.0":
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.80.0.tgz#be1f7c1f1093490735fbd2bf6a218585130742b5"
+  integrity sha512-4U7z72/yJKhswdnGSG/0AALzLfGn6XHaRlQ8QK63MKYbpEjZeZ0p/Rc9wmngPo8CU7YKXSJeIdsArMZq/pqSlw==
 
 "@douyinfe/[email protected]":
   version "2.65.0"
@@ -1618,10 +1618,10 @@
   dependencies:
     bezier-easing "^2.1.0"
 
-"@douyinfe/semi-animation@2.78.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.78.0.tgz#a76be1ff65486d5146e78fdafffd5a6ab1b818e6"
-  integrity sha512-M54Typ1mHU4BIT/em/WlpmKM0V1yk2EFoVy9qtTr0+vkIgTfE+w53A7/NX2cyWq97FHCD9k4jQGGK31Z1YqMQg==
+"@douyinfe/[email protected]0.0":
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.80.0.tgz#37515eb0c051ff1f6cb7196c4d2a15bad644020a"
+  integrity sha512-wjJr6EbO5TkJcyqx/CyLUdIE62TStfmCVDeMPXUT709OnIecoU1BsB/LGMVsdNQXF0muxb8bBcEb/A9M5mMclQ==
   dependencies:
     bezier-easing "^2.1.0"
 
@@ -1644,13 +1644,13 @@
     remark-gfm "^4.0.0"
     scroll-into-view-if-needed "^2.2.24"
 
-"@douyinfe/semi-foundation@2.78.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.78.0.tgz#d1949b552ff75bfe66a88519fdfbf97dfd71216f"
-  integrity sha512-LVBcVUDM74FMmzx/L6b/Vk/W9b4MnQCb1+1t7Q2BU3v5DC4dfhgRpbloxj3JmAyOgAcxJz2VDE07Sgzt9mS6ww==
+"@douyinfe/[email protected]0.0":
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.80.0.tgz#1c2278fdd011d20d4c5223f3ad9e3e3cc955d8aa"
+  integrity sha512-wtrdBXzF3tU1mIKxUgRHH6qhLSlpQhNAXDnnA00JMo4/hXfINzLFd3dOjQpaWxu8TIDq/UF8rDS/Y/1tlmsXzg==
   dependencies:
-    "@douyinfe/semi-animation" "2.78.0"
-    "@douyinfe/semi-json-viewer-core" "2.78.0"
+    "@douyinfe/semi-animation" "2.80.0"
+    "@douyinfe/semi-json-viewer-core" "2.80.0"
     "@mdx-js/mdx" "^3.0.1"
     async-validator "^3.5.0"
     classnames "^2.2.6"
@@ -1671,10 +1671,10 @@
   dependencies:
     classnames "^2.2.6"
 
-"@douyinfe/semi-icons@2.78.0", "@douyinfe/semi-icons@^2.0.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.78.0.tgz#aa0f852939f9eef071d17d2c3894dabf2e4b87d4"
-  integrity sha512-8Sugn7lgAqgHThX3EJT9npyQ2tXFIEZoV6KkYMNFdewOlZ23O1VYEHwh4ktdNVsgtw5CTQ5+UQrsuM3mqszAVw==
+"@douyinfe/[email protected]0.0", "@douyinfe/semi-icons@^2.0.0":
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.80.0.tgz#a4411181fc867fcb46371dab82bb9ed1167f4e8f"
+  integrity sha512-V7fyPdbws/lT3nK/4aneSRgDHEx3AG4njAAEFBW1oOnc/T1+xYbgoDzOlK9lC6DpBzw42/KRrpMrQ236gIRf2w==
   dependencies:
     classnames "^2.2.6"
 
@@ -1683,15 +1683,15 @@
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.65.0.tgz#9916c540c91222a1d9f48cd34a941d28b8a05d2f"
   integrity sha512-1IhOztyBYiSu8WrcvN+oWWtcJTC9+x6zbnYtufx4ToISs5UO1te1PQofABpkDzIJYFtW9yYLxg4uoL4wGjqYMA==
 
-"@douyinfe/semi-illustrations@2.78.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.78.0.tgz#af2a6188d36b156bb716005a4ee01419eadf4b06"
-  integrity sha512-690Llbnqsf1OZYuFvHhFWLpJkj6Gk5fjnWjJzUUcgfS5Apn/FBRHH2tkGyn4eU2iBihta1zzC6/G7Orp7qID5A==
+"@douyinfe/[email protected]0.0":
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.80.0.tgz#7b59dd859c040114a644711fb1e0d0a145b077e4"
+  integrity sha512-BIgp37ZlVPoyzmClp8UlAOIrJv5SAak61HWZg9DRvckZ2Fm5f39Bz/QJNf15rIiEQKvCobsR37EuECnCYLJ1UQ==
 
-"@douyinfe/semi-json-viewer-core@2.78.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-json-viewer-core/-/semi-json-viewer-core-2.78.0.tgz#046b07c4f51db386478175f661484f5f147fb8b5"
-  integrity sha512-htbOfZp079wxoKaZkGwxd7pFfxU+tfzihGMmTy3b3Dneut3cPQmRVkyCSbJjuYwOkNEyHQ7Cva6KFbNhfeKxqw==
+"@douyinfe/[email protected]0.0":
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-json-viewer-core/-/semi-json-viewer-core-2.80.0.tgz#93dd25bcfabdacefd008f0318b0c49dcdc0836a7"
+  integrity sha512-SU+EsMr3xsxzNvi1QngB9KhfVdXoa9M7rvpw7o0MYnUGE7jFBGICwwh3PKGxOGJqLNjngartgxkFfH3BcCaFgA==
   dependencies:
     jsonc-parser "^3.3.1"
 
@@ -1766,25 +1766,25 @@
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.61.0.tgz#a7e9bf9534721c12af1d0eeb5d5a2de615896a23"
   integrity sha512-obn/DOw4vZyKFAlWvZxHTpBLAK9FO9kygTSm2GROgvi+UDB2PPU6l20cuUCsdGUNWJRSqYlTTVZ1tNYIyFZ5Sg==
 
-"@douyinfe/semi-theme-default@2.78.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.78.0.tgz#009d711196b5a15134afbffc14c088368db1e058"
-  integrity sha512-LtF6G+cmGNNjDE08K5VCG2n2cOi/hPIBTCfvwwXot8druEw94RGQ16rnAKNDMhASWJbiAzaVWKGhDQLDCwfZvg==
+"@douyinfe/[email protected]0.0":
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.80.0.tgz#60ab77cfafea9a353227ec8a1fa5982308372973"
+  integrity sha512-pbQYIJhESsAlva4+syfViHZkayTTL3bMLIjNT1IAdu8fhclCFXXRBNhTFLobZ8qxhlkSyCdFoYer5oSkFoUTDw==
 
 "@douyinfe/semi-ui@^2.0.0":
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.78.0.tgz#5a8fdcac557932e3e24c3c495ef81f98638baadc"
-  integrity sha512-S8AvtfwLwb147mRzX83WmnW4op5YXoD0RSdnDzWrWDNNQyszV7VkV6+f2VIg27e4rReexYkMG1SzJLdzgoZKGQ==
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.80.0.tgz#482c22a89b0ecae14a8b0630da359f0f4596b982"
+  integrity sha512-sMYRSGt7peXb6R7PDwsnAoICI3U9Ao2vcOaV2NHuRz6G5NFJJ9svKEup3ehpOgdU2HcAa3mIOh8wvnVYOVoFDg==
   dependencies:
     "@dnd-kit/core" "^6.0.8"
     "@dnd-kit/sortable" "^7.0.2"
     "@dnd-kit/utilities" "^3.2.1"
-    "@douyinfe/semi-animation" "2.78.0"
-    "@douyinfe/semi-animation-react" "2.78.0"
-    "@douyinfe/semi-foundation" "2.78.0"
-    "@douyinfe/semi-icons" "2.78.0"
-    "@douyinfe/semi-illustrations" "2.78.0"
-    "@douyinfe/semi-theme-default" "2.78.0"
+    "@douyinfe/semi-animation" "2.80.0"
+    "@douyinfe/semi-animation-react" "2.80.0"
+    "@douyinfe/semi-foundation" "2.80.0"
+    "@douyinfe/semi-icons" "2.80.0"
+    "@douyinfe/semi-illustrations" "2.80.0"
+    "@douyinfe/semi-theme-default" "2.80.0"
     async-validator "^3.5.0"
     classnames "^2.2.6"
     copy-text-to-clipboard "^2.1.1"
@@ -12236,9 +12236,9 @@ eslint-plugin-react@^7.20.6, eslint-plugin-react@^7.24.0:
     string.prototype.repeat "^1.0.0"
 
 eslint-plugin-semi-design@^2.33.0:
-  version "2.78.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-semi-design/-/eslint-plugin-semi-design-2.78.0.tgz#066c79b8e2e0bbb0cc7d8ed2ac8811dd601bd289"
-  integrity sha512-1+ZltNfA/zx1ipMaqa+gr/yTArpx2aeaXb4UXz6omUzlmgFDERbIUZ1P1n6+bbOCu8tAJ4CaD3gnwlDFBcamGQ==
+  version "2.80.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-semi-design/-/eslint-plugin-semi-design-2.80.0.tgz#e4f72e9e38b359eb98a55b4ca6e9cef15270b4fe"
+  integrity sha512-v2Gr32kb7731b/wTlIwQMbsORweMlOhICWD5mBGEz/WUM3O60gUUmDBjj8pzpMbKPW0rZ03Q1wEqmTm5HjEa+g==
 
 eslint-rule-composer@^0.3.0:
   version "0.3.0"
@@ -25495,7 +25495,7 @@ string-similarity@^1.2.2:
     lodash.map "^4.6.0"
     lodash.maxby "^4.6.0"
 
-"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -25513,6 +25513,15 @@ string-width@^1.0.1, string-width@^1.0.2:
     is-fullwidth-code-point "^1.0.0"
     strip-ansi "^3.0.0"
 
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
 string-width@^2.0.0, string-width@^2.1.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
@@ -25656,7 +25665,7 @@ stringify-object@^3.3.0:
     is-obj "^1.0.1"
     is-regexp "^1.0.0"
 
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -25684,6 +25693,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
   dependencies:
     ansi-regex "^4.1.0"
 
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
 strip-ansi@^7.0.1:
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -28302,7 +28318,7 @@ worker-loader@^3.0.8:
     loader-utils "^2.0.0"
     schema-utils "^3.0.0"
 
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -28337,6 +28353,15 @@ wrap-ansi@^6.2.0:
     string-width "^4.1.0"
     strip-ansi "^6.0.0"
 
+wrap-ansi@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
 wrap-ansi@^8.1.0:
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"

Some files were not shown because too many files changed in this diff