Browse Source

Add tooltips to player controls (#1015)

* add VideoControlWithTooltip

* refactor

* use css for tooltips

* lint fix

* transition fix

* lint
Bartosz Dryl 3 years ago
parent
commit
54e99952c7

+ 64 - 3
src/shared/components/VideoPlayer/VideoPlayer.style.tsx

@@ -15,6 +15,12 @@ type CustomControlsProps = {
   isEnded?: boolean
   isEnded?: boolean
 }
 }
 
 
+type ControlButtonProps = {
+  tooltipText?: string
+  tooltipPosition?: 'left' | 'right'
+  showTooltipOnlyOnFocus?: boolean
+}
+
 const focusStyles = css`
 const focusStyles = css`
   :focus {
   :focus {
     /* Provide a fallback style for browsers
     /* Provide a fallback style for browsers
@@ -58,7 +64,7 @@ export const CustomControls = styled.div<CustomControlsProps>`
   transition: transform 200ms ${transitions.easing}, opacity 200ms ${transitions.easing};
   transition: transform 200ms ${transitions.easing}, opacity 200ms ${transitions.easing};
 `
 `
 
 
-export const ControlButton = styled.button`
+export const ControlButton = styled.button<ControlButtonProps>`
   margin-right: 0.5em;
   margin-right: 0.5em;
   display: flex !important;
   display: flex !important;
   cursor: pointer;
   cursor: pointer;
@@ -68,7 +74,8 @@ export const ControlButton = styled.button`
   align-items: center;
   align-items: center;
   justify-content: center;
   justify-content: center;
   padding: 0.5em;
   padding: 0.5em;
-  transition: background-color ${transitions.timings.player} ${transitions.easing} !important;
+  position: relative;
+  transition: background ${transitions.timings.player} ease-in !important;
 
 
   & > svg {
   & > svg {
     filter: drop-shadow(0 1px 2px ${colors.transparentBlack[32]});
     filter: drop-shadow(0 1px 2px ${colors.transparentBlack[32]});
@@ -76,15 +83,67 @@ export const ControlButton = styled.button`
     height: 1.5em;
     height: 1.5em;
   }
   }
 
 
+  ::before {
+    ${({ tooltipPosition }) => tooltipPosition === 'left' && 'left: 0'};
+    ${({ tooltipPosition }) => tooltipPosition === 'right' && 'right: 0'};
+
+    opacity: 0;
+    pointer-events: none;
+    content: ${({ tooltipText }) => tooltipText && `'${tooltipText}'`};
+    position: absolute;
+    font-size: 0.875em;
+    bottom: calc(3.5em - 1px);
+    background: ${colors.transparentBlack[54]};
+    backdrop-filter: blur(${sizes(8)});
+    display: flex;
+    align-items: center;
+    padding: 0.5em;
+    white-space: nowrap;
+    transition: opacity ${transitions.timings.player} ease-in !important;
+  }
+
   :hover {
   :hover {
     background-color: ${colors.transparentPrimary[18]};
     background-color: ${colors.transparentPrimary[18]};
     backdrop-filter: blur(${sizes(8)});
     backdrop-filter: blur(${sizes(8)});
+    transition: none !important;
+
+    ::before {
+      transition: none !important;
+      opacity: ${({ showTooltipOnlyOnFocus }) => (showTooltipOnlyOnFocus ? 0 : 1)};
+    }
   }
   }
 
 
   :active {
   :active {
     background-color: ${colors.transparentPrimary[10]};
     background-color: ${colors.transparentPrimary[10]};
   }
   }
 
 
+  :focus {
+    ::before {
+      /* turn off transition on mouse enter, but turn on on mouse leave */
+      transition: none !important;
+      opacity: 1;
+    }
+  }
+
+  :focus-visible {
+    ::before {
+      opacity: 1;
+    }
+  }
+
+  :focus:not(:focus-visible):hover {
+    ::before {
+      transition: none !important;
+      opacity: ${({ showTooltipOnlyOnFocus }) => (showTooltipOnlyOnFocus ? 0 : 1)};
+    }
+  }
+
+  :focus:not(:focus-visible):not(:hover) {
+    ::before {
+      opacity: 0;
+    }
+  }
+
   ${focusStyles}
   ${focusStyles}
 `
 `
 
 
@@ -183,8 +242,10 @@ export const CurrentTime = styled(Text)`
 `
 `
 
 
 export const ScreenControls = styled.div`
 export const ScreenControls = styled.div`
+  display: grid;
+  grid-template-columns: auto auto;
+  gap: 0.5em;
   margin-left: auto;
   margin-left: auto;
-  display: flex;
 
 
   ${ControlButton}:last-of-type {
   ${ControlButton}:last-of-type {
     margin-right: 0;
     margin-right: 0;

+ 14 - 4
src/shared/components/VideoPlayer/VideoPlayer.tsx

@@ -427,11 +427,17 @@ const VideoPlayerComponent: React.ForwardRefRenderFunction<HTMLVideoElement, Vid
           <>
           <>
             <ControlsOverlay isFullScreen={isFullScreen}>
             <ControlsOverlay isFullScreen={isFullScreen}>
               <CustomControls isFullScreen={isFullScreen} isEnded={playerState === 'ended'}>
               <CustomControls isFullScreen={isFullScreen} isEnded={playerState === 'ended'}>
-                <ControlButton onClick={handlePlayPause}>
+                <ControlButton
+                  onClick={handlePlayPause}
+                  tooltipText={isPlaying ? 'Pause (k)' : 'Play (k)'}
+                  tooltipPosition="left"
+                >
                   {playerState === 'ended' ? <SvgPlayerRestart /> : isPlaying ? <SvgPlayerPause /> : <SvgPlayerPlay />}
                   {playerState === 'ended' ? <SvgPlayerRestart /> : isPlaying ? <SvgPlayerPause /> : <SvgPlayerPlay />}
                 </ControlButton>
                 </ControlButton>
                 <VolumeControl>
                 <VolumeControl>
-                  <VolumeButton onClick={handleMute}>{renderVolumeButton()}</VolumeButton>
+                  <VolumeButton onClick={handleMute} showTooltipOnlyOnFocus tooltipText="Volume">
+                    {renderVolumeButton()}
+                  </VolumeButton>
                   <VolumeSliderContainer>
                   <VolumeSliderContainer>
                     <VolumeSlider
                     <VolumeSlider
                       step={0.01}
                       step={0.01}
@@ -450,11 +456,15 @@ const VideoPlayerComponent: React.ForwardRefRenderFunction<HTMLVideoElement, Vid
                 </CurrentTimeWrapper>
                 </CurrentTimeWrapper>
                 <ScreenControls>
                 <ScreenControls>
                   {isPiPSupported && (
                   {isPiPSupported && (
-                    <ControlButton onClick={handlePictureInPicture}>
+                    <ControlButton onClick={handlePictureInPicture} tooltipText="Picture-in-picture">
                       {isPiPEnabled ? <SvgPlayerPipDisable /> : <SvgPlayerPip />}
                       {isPiPEnabled ? <SvgPlayerPipDisable /> : <SvgPlayerPip />}
                     </ControlButton>
                     </ControlButton>
                   )}
                   )}
-                  <ControlButton onClick={handleFullScreen}>
+                  <ControlButton
+                    onClick={handleFullScreen}
+                    tooltipPosition="right"
+                    tooltipText={isFullScreen ? 'Exit full screen (f)' : 'Full screen (f)'}
+                  >
                     {isFullScreen ? <SvgPlayerSmallScreen /> : <SvgPlayerFullScreen />}
                     {isFullScreen ? <SvgPlayerSmallScreen /> : <SvgPlayerFullScreen />}
                   </ControlButton>
                   </ControlButton>
                 </ScreenControls>
                 </ScreenControls>