Browse Source

Add Icon Component

Francesco Baccetti 4 years ago
parent
commit
08a13d8850

+ 2 - 2
packages/app/src/shared/components/Button/Button.style.ts

@@ -1,7 +1,7 @@
 import React from 'react'
 import { css } from '@emotion/core'
 import styled from '@emotion/styled'
-import { BlockIcon } from '@/shared/icons'
+import Icon from '../Icon'
 import { typography, colors } from '../../theme'
 
 export type ButtonStyleProps = {
@@ -120,7 +120,7 @@ const disabled = ({ disabled }: ButtonStyleProps) =>
       `
     : null
 
-export const StyledIcon = styled(BlockIcon)`
+export const StyledIcon = styled(Icon)`
   flex-shrink: 0;
   & > * {
     stroke: currentColor;

+ 3 - 2
packages/app/src/shared/components/Button/Button.tsx

@@ -1,9 +1,10 @@
 import React from 'react'
 import { SerializedStyles } from '@emotion/core'
 import { ButtonStyleProps, StyledButton, StyledIcon } from './Button.style'
+import type { IconType } from '../Icon'
 
 export type ButtonProps = {
-  icon: boolean
+  icon: IconType
   disabled: boolean
   containerCss: SerializedStyles
   className: string
@@ -40,7 +41,7 @@ const Button: React.FC<Partial<ButtonProps>> = ({
       full={full}
       size={size}
     >
-      {icon && <StyledIcon />}
+      {icon && <StyledIcon name={icon} />}
       {children && <span>{children}</span>}
     </StyledButton>
   )

+ 2 - 2
packages/app/src/shared/components/Checkbox/Checkbox.tsx

@@ -1,6 +1,6 @@
 import React from 'react'
 import { CheckboxStyleProps, useCSS } from './Checkbox.style'
-import { CheckIcon, DashIcon } from '../../icons'
+import Icon from '../Icon'
 
 type CheckboxProps = {
   label?: string
@@ -25,7 +25,7 @@ export default function Checkbox({
       <div css={styles.outerContainer}>
         <div css={styles.innerContainer}>
           <input css={styles.input} type="checkbox" checked={selected} disabled={disabled} onChange={onChange} />
-          {selected && (icon === 'check' ? <CheckIcon css={styles.icon} /> : <DashIcon css={styles.icon} />)}
+          {selected && <Icon name={icon === 'check' ? 'check' : 'dash'} css={styles.icon} />}
         </div>
       </div>
       {(labelPosition === 'end' || labelPosition === 'bottom') && <label css={styles.label}>{label}</label>}

+ 5 - 3
packages/app/src/shared/components/Dropdown/Dropdown.tsx

@@ -1,6 +1,6 @@
 import React, { useRef, useState } from 'react'
 import { DropdownStyleProps, useCSS } from './Dropdown.style'
-import { ChevronIconDown, ChevronIconUp } from '../../icons'
+import Icon from '../Icon'
 import { spacing } from '../../theme'
 
 type DropdownOption = {
@@ -73,8 +73,10 @@ export default function Dropdown({
             disabled={true}
             value={inputTextValue}
           />
-          {!showOptions && <ChevronIconUp css={styles.iconOpen} />}
-          {!!showOptions && <ChevronIconDown css={styles.iconClose} />}
+          <Icon
+            name={showOptions ? 'chevron-up' : 'chevron-down'}
+            css={showOptions ? styles.iconClose : styles.iconOpen}
+          />
         </div>
         {showOptions && (
           <div css={styles.options}>

+ 34 - 0
packages/app/src/shared/components/Icon/Icon.tsx

@@ -0,0 +1,34 @@
+import React from 'react'
+import { css } from '@emotion/core'
+import * as Icons from '../../icons'
+import { camelCase } from 'lodash'
+
+type IconType = Icons.IconType
+type IconProps = {
+  name: IconType
+} & React.SVGProps<SVGSVGElement>
+
+const capitalize = (s: string) => s.slice(0, 1).toUpperCase() + s.slice(1)
+const pascalCase = (s: string) => capitalize(camelCase(s))
+const iconsList = Object.keys(Icons)
+const iconStyles = css`
+  stroke: currentColor;
+  > * {
+    stroke: currentColor;
+  }
+`
+
+const Icon: React.FC<IconProps> = ({ name, ...svgProps }) => {
+  const iconProp = pascalCase(name) as keyof typeof Icons
+  if (!iconsList.includes(iconProp)) {
+    console.warn(`Icon ${iconProp} is not a valid icon and will not render.`)
+    return null
+  }
+
+  const IconComponent = Icons[iconProp]
+
+  return <IconComponent css={iconStyles} {...svgProps} />
+}
+
+export default Icon
+export type { IconType }

+ 3 - 0
packages/app/src/shared/components/Icon/index.ts

@@ -0,0 +1,3 @@
+import Icon, { IconType } from './Icon'
+export default Icon
+export type { IconType }

+ 2 - 2
packages/app/src/shared/components/NavButton/NavButton.tsx

@@ -1,7 +1,7 @@
 import React from 'react'
 import { SerializedStyles } from '@emotion/core'
 import { NavButtonStyleProps, useCSS } from './NavButton.style'
-import { ChevronLeftIcon, ChevronRightIcon } from '../../icons'
+import Icon from '../Icon'
 
 type NavButtonProps = {
   direction: 'right' | 'left'
@@ -13,7 +13,7 @@ export default function NavButton({ direction = 'right', onClick, outerCss, ...s
   const styles = useCSS(styleProps)
   return (
     <button css={[styles, outerCss]} onClick={onClick}>
-      {direction === 'right' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
+      <Icon name={direction === 'right' ? 'chevron-right' : 'chevron-left'} />
     </button>
   )
 }

+ 15 - 0
packages/app/src/shared/components/ToggleButton/ToggleButton.styles.tsx

@@ -115,6 +115,7 @@ const disabled = ({ disabled }: ToggleButtonStyleProps) =>
       `
     : null
 
+<<<<<<< HEAD
 export const StyledToggleButton = styled(Button)`
      transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
       ${colorsFromProps}
@@ -122,3 +123,17 @@ export const StyledToggleButton = styled(Button)`
       ${hoverTransition}
       ${disabled}
       `
+=======
+export const StyledToggleButton: React.FC<ToggleButtonStyleProps> = (props) => (
+  <Button
+    {...props}
+    containerCss={css`
+     transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
+      ${colorsFromProps(props)}
+      ${pressed(props)}
+      ${hoverTransition(props)}
+      ${disabled(props)}
+      `}
+  />
+)
+>>>>>>> Create ToggleButton And Add Animation To ChannelPreview

+ 5 - 2
packages/app/src/shared/components/VideoPlayer/VideoPlayer.style.tsx

@@ -1,6 +1,7 @@
+import React from 'react'
 import styled from '@emotion/styled'
 import { colors, spacing, typography } from '../../theme'
-import { PlayIcon } from '../../icons'
+import Icon from '../Icon'
 
 export const Container = styled.div`
   position: relative;
@@ -134,7 +135,9 @@ export const PlayOverlay = styled.div`
   cursor: pointer;
 `
 
-export const StyledPlayIcon = styled(PlayIcon)`
+const StyledIcon = styled(Icon)`
   height: 72px;
   width: 72px;
+  color: ${colors.white};
 `
+export const StyledPlayIcon = ({ ...svgProps }) => <StyledIcon name="play" {...svgProps} />

+ 7 - 3
packages/app/src/shared/components/VideoPreview/VideoPreview.styles.tsx

@@ -1,7 +1,8 @@
+import React from 'react'
 import styled from '@emotion/styled'
 import { colors, spacing, typography } from '../../theme'
 import Avatar from '../Avatar'
-import { PlayIcon } from '../../icons'
+import Icon from '../Icon'
 import { HOVER_BORDER_SIZE } from './VideoPreviewBase.styles'
 
 type CoverImageProps = Record<string, unknown>
@@ -38,13 +39,16 @@ export const CoverHoverOverlay = styled.div`
   align-items: center;
 `
 
-export const CoverPlayIcon = styled(PlayIcon)`
-  transition: transform 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
+export const CoverIcon = styled(Icon)`
   transform: translateY(40px);
+  transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
   width: 54px;
   height: 54px;
+  color: ${colors.white};
 `
 
+export const CoverPlayIcon = ({ ...props }) => <CoverIcon name="play" {...props} />
+
 export const ProgressOverlay = styled.div`
   position: absolute;
   left: 0;

+ 2 - 2
packages/app/src/shared/components/VideoPreview/VideoPreviewBase.styles.tsx

@@ -1,6 +1,6 @@
 import styled from '@emotion/styled'
 import { colors, spacing } from '@/shared/theme'
-import { CoverHoverOverlay, CoverPlayIcon, ProgressOverlay } from './VideoPreview.styles'
+import { CoverHoverOverlay, CoverIcon, ProgressOverlay } from './VideoPreview.styles'
 
 export const HOVER_BORDER_SIZE = '2px'
 
@@ -39,7 +39,7 @@ export const Container = styled.article<ContainerProps>`
 						opacity: 1;
 					}
 					
-					${CoverPlayIcon} {
+					${CoverIcon} {
 						transform: translateY(0);
 					}
 

+ 1 - 0
packages/app/src/shared/components/index.ts

@@ -26,3 +26,4 @@ export { default as GlobalStyle } from './GlobalStyle'
 export { default as Placeholder } from './Placeholder'
 export { default as InfiniteVideoGrid } from './InfiniteVideoGrid'
 export { default as ToggleButton } from './ToggleButton'
+export { default as Icon } from './Icon'

+ 31 - 13
packages/app/src/shared/icons/index.ts

@@ -1,13 +1,31 @@
-export { ReactComponent as BarsIcon } from './bars.svg'
-export { ReactComponent as HomeIcon } from './home.svg'
-export { ReactComponent as BinocularIcon } from './binocular.svg'
-export { ReactComponent as BrowseIcon } from './browse.svg'
-export { ReactComponent as BooksIcon } from './books.svg'
-export { ReactComponent as BlockIcon } from './block.svg'
-export { ReactComponent as ChevronIconUp } from './chevron-down-big.svg'
-export { ReactComponent as ChevronIconDown } from './chevron-up-big.svg'
-export { ReactComponent as ChevronRightIcon } from './chevron-right-big.svg'
-export { ReactComponent as ChevronLeftIcon } from './chevron-left-big.svg'
-export { ReactComponent as CheckIcon } from './check.svg'
-export { ReactComponent as DashIcon } from './dash.svg'
-export { ReactComponent as PlayIcon } from './play.svg'
+export { ReactComponent as Bars } from './bars.svg'
+export { ReactComponent as Home } from './home.svg'
+export { ReactComponent as Binocular } from './binocular.svg'
+export { ReactComponent as Browse } from './browse.svg'
+export { ReactComponent as Books } from './books.svg'
+export { ReactComponent as Block } from './block.svg'
+export { ReactComponent as ChevronDown } from './chevron-down-big.svg'
+export { ReactComponent as ChevronUp } from './chevron-up-big.svg'
+export { ReactComponent as ChevronRight } from './chevron-right-big.svg'
+export { ReactComponent as ChevronLeft } from './chevron-left-big.svg'
+export { ReactComponent as Check } from './check.svg'
+export { ReactComponent as Dash } from './dash.svg'
+export { ReactComponent as Play } from './play.svg'
+
+const icons = [
+  'bars',
+  'home',
+  'binocular',
+  'browse',
+  'books',
+  'block',
+  'chevron-down',
+  'chevron-up',
+  'chevron-right',
+  'chevron-left',
+  'check',
+  'dash',
+  'play',
+] as const
+
+export type IconType = typeof icons[number]

+ 14 - 14
packages/app/src/shared/stories/01-Button.stories.tsx

@@ -45,11 +45,11 @@ export const SecondaryFullSize = () => (
 
 export const PrimaryWithIcon = () => (
   <>
-    <Button icon>Regular</Button>
-    <Button icon size="small">
+    <Button icon="block">Regular</Button>
+    <Button icon="block" size="small">
       Small
     </Button>
-    <Button icon size="smaller">
+    <Button icon="block" size="smaller">
       Smaller
     </Button>
   </>
@@ -57,13 +57,13 @@ export const PrimaryWithIcon = () => (
 
 export const SecondaryWithIcon = () => (
   <>
-    <Button variant="secondary" icon>
+    <Button variant="secondary" icon="block">
       Regular
     </Button>
-    <Button variant="secondary" icon size="small">
+    <Button variant="secondary" icon="block" size="small">
       Small
     </Button>
-    <Button variant="secondary" icon size="smaller">
+    <Button variant="secondary" icon="block" size="smaller">
       Smaller
     </Button>
   </>
@@ -71,17 +71,17 @@ export const SecondaryWithIcon = () => (
 
 export const PrimaryWithoutText = () => (
   <>
-    <Button icon />
-    <Button icon size="small" />
-    <Button icon size="smaller" />
+    <Button icon="block" />
+    <Button icon="block" size="small" />
+    <Button icon="block" size="smaller" />
   </>
 )
 
 export const SecondaryWithoutText = () => (
   <>
-    <Button variant="secondary" icon />
-    <Button variant="secondary" icon size="small" />
-    <Button variant="secondary" icon size="smaller" />
+    <Button variant="secondary" icon="block" />
+    <Button variant="secondary" icon="block" size="small" />
+    <Button variant="secondary" icon="block" size="smaller" />
   </>
 )
 
@@ -90,9 +90,9 @@ export const Disabled = () => (
     <Button disabled={true} onClick={() => action('Clicked a disabled button, this should not happen.')}>
       Disabled
     </Button>
-    <Button disabled={true} icon={true}>
+    <Button disabled={true} icon="block">
       Disabled with icon
     </Button>
-    <Button disabled={true} icon />
+    <Button disabled={true} icon="block" />
   </>
 )

+ 14 - 14
packages/app/src/shared/stories/03-ToggleButton.stories.tsx

@@ -45,11 +45,11 @@ export const SecondaryFullSize = () => (
 
 export const PrimaryWithIcon = () => (
   <>
-    <ToggleButton icon>Regular</ToggleButton>
-    <ToggleButton icon size="small">
+    <ToggleButton icon="block">Regular</ToggleButton>
+    <ToggleButton icon="block" size="small">
       Small
     </ToggleButton>
-    <ToggleButton icon size="smaller">
+    <ToggleButton icon="block" size="smaller">
       Smaller
     </ToggleButton>
   </>
@@ -57,13 +57,13 @@ export const PrimaryWithIcon = () => (
 
 export const SecondaryWithIcon = () => (
   <>
-    <ToggleButton variant="secondary" icon>
+    <ToggleButton variant="secondary" icon="block">
       Regular
     </ToggleButton>
-    <ToggleButton variant="secondary" icon size="small">
+    <ToggleButton variant="secondary" icon="block" size="small">
       Small
     </ToggleButton>
-    <ToggleButton variant="secondary" icon size="smaller">
+    <ToggleButton variant="secondary" icon="block" size="smaller">
       Smaller
     </ToggleButton>
   </>
@@ -71,17 +71,17 @@ export const SecondaryWithIcon = () => (
 
 export const PrimaryWithoutText = () => (
   <>
-    <ToggleButton icon />
-    <ToggleButton icon size="small" />
-    <ToggleButton icon size="smaller" />
+    <ToggleButton icon="block" />
+    <ToggleButton icon="block" size="small" />
+    <ToggleButton icon="block" size="smaller" />
   </>
 )
 
 export const SecondaryWithoutText = () => (
   <>
-    <ToggleButton variant="secondary" icon />
-    <ToggleButton variant="secondary" icon size="small" />
-    <ToggleButton variant="secondary" icon size="smaller" />
+    <ToggleButton variant="secondary" icon="block" />
+    <ToggleButton variant="secondary" icon="block" size="small" />
+    <ToggleButton variant="secondary" icon="block" size="smaller" />
   </>
 )
 
@@ -90,9 +90,9 @@ export const Disabled = () => (
     <ToggleButton disabled={true} onClick={() => action('Clicked a disabled ToggleButton, this should not happen.')}>
       Disabled
     </ToggleButton>
-    <ToggleButton disabled={true} icon={true}>
+    <ToggleButton disabled={true} icon="block">
       Disabled with icon
     </ToggleButton>
-    <ToggleButton disabled={true} icon />
+    <ToggleButton disabled={true} icon="block" />
   </>
 )

+ 4 - 5
packages/app/src/shared/stories/15-Sidenav.stories.tsx

@@ -1,7 +1,6 @@
 import React from 'react'
 import styled from '@emotion/styled'
-import { NavItem, Sidenav, SIDENAV_WIDTH } from '../components'
-import { BinocularIcon, BrowseIcon, HomeIcon } from '../icons'
+import { NavItem, Sidenav, SIDENAV_WIDTH, Icon } from '../components'
 
 export default {
   title: 'Sidenav',
@@ -11,11 +10,11 @@ export default {
 const NAV_ITEMS: NavItem[] = [
   {
     name: 'Home',
-    icon: <HomeIcon />,
+    icon: <Icon name="home" />,
   },
   {
     name: 'Discover',
-    icon: <BinocularIcon />,
+    icon: <Icon name="binocular" />,
     subitems: [
       {
         name: 'Channels 1',
@@ -36,7 +35,7 @@ const NAV_ITEMS: NavItem[] = [
   },
   {
     name: 'Browse',
-    icon: <BrowseIcon />,
+    icon: <Icon icon="browse" />,
     subitems: [
       {
         name: 'Channels',