TS+react自建组件库 03

TS+react自建组件库 03

KGD的第五个组件-Icon

引用第三方图标库:fontowesome

安装指令:

yarn add @fortawesome/fontawesome-svg-core
yarn add @fortawesome/free-solid-svg-icons
yarn add @fortawesome/react-fontawesome

引入:

import {FontAwesomeIcon, FontAwesomeIconProps} from '@fortawesome/react-fontawesome'
import {library} from '@fortawesome/fontawesome-svg-core'
import {fas} from '@fortawesome/free-solid-svg-icons'
library.add(fas)

实现:

import {FC} from 'react'
import classNames from 'classnames'

import {FontAwesomeIcon, FontAwesomeIconProps} from '@fortawesome/react-fontawesome'
import {library} from '@fortawesome/fontawesome-svg-core'
import {fas} from '@fortawesome/free-solid-svg-icons'
library.add(fas)

export type ThemeProps = 'success' | 'danger' | 'warning' | 'info' | 'primary' | 'secondary' | 'light' | 'dark'

export interface IconProps extends FontAwesomeIconProps {
  theme ?: ThemeProps
}

const Icon : FC<IconProps> = (props) => {

  const {className, theme, ...restProps} = props

  const classes = classNames('kgd-icon', className, {
    [`icon-${theme}`] : theme
  })

  return(
    <FontAwesomeIcon 
    className = {classes} 
    {...restProps}
    />
  )
}

export default Icon;

KGD的第六个组件-Transition

引用react库 : react-transition-group

安装指令:

yarn add react-transition-group  @types/react-transition-group --save

引入:

import {CSSTransition} from 'react-transition-group'
import {CSSTransitionProps} from 'react-transition-group/CSSTransition'

实现:

import {FC} from 'react'

import {CSSTransition} from 'react-transition-group'
import {CSSTransitionProps} from 'react-transition-group/CSSTransition'

type AnimationName = 'zoom-in-top' | 'zoom-in-left' | 'zoom-in-bottom' | 'zoom-in-right'

type TransitionProps =  CSSTransitionProps & {
  animation ?: AnimationName
} 

const Transition : FC<TransitionProps> = (props) => {

  const {
    classNames,
    children,
    animation,
    ...restProps
  } = props

  return(
    <CSSTransition
      classNames = {classNames ? classNames : animation}
      {...restProps}
    >
      {children}
    </CSSTransition>
  )
}

export default Transition

使用storybook

安装指令:

npx sb init

运行:

yarn storybook

组件.stories.tsx编写:

import {FC} from 'react'
import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'

引入全局样式:

//URL:.storybook/preview.js

// .storybook/preview.js
import { addDecorator } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import '../src/styles/index.scss'
addDecorator(withInfo); 

补全表格样式:

//_infoTable.scss
.info-table {
  border-collapse: separate;
  tr th,
  td {
  padding: 4px;
  border: 1px solid gray;
  }
  width:100%;
}

设置实现:

以Tabs为例:

import { storiesOf } from '@storybook/react'

import Tabs from './'
import TabItem from './TabItem'
import Icon from '../Icon'

const defaultTabs = () => (
  <div
    style={{
      padding: '20px 40px',
      width: '500px'
    }}
  >
    <Tabs
    defaultIndex={0}
    onSelect={function noRefCheck(){}}
    type="line"
  >
    <TabItem label="选项卡一">
      this is content one
    </TabItem>
    <TabItem label="选项卡二">
      this is content two
    </TabItem>
    <TabItem label="用户管理">
      this is content three
    </TabItem>
  </Tabs>
  </div>
)

const CardTabs = () => (
  <div
    style={{
      padding: '20px 40px',
      width: '500px'
    }}
  >
    <Tabs
    defaultIndex={0}
    onSelect={function noRefCheck(){}}
    type="card"
  >
    <TabItem label="card1">
      this is card one
    </TabItem>
    <TabItem label="card2">
      this is content two
    </TabItem>
    <TabItem
      disabled
      label="disabled"
    >
      this is content three
    </TabItem>
  </Tabs>
  </div>
)

const CustomCardTabs = () => (
  <div
    style={{
      padding: '20px 40px',
      width: '500px'
    }}
  >
    <Tabs
    defaultIndex={0}
    onSelect={function noRefCheck(){}}
    type="card"
  >
    <TabItem label={<><Icon icon="exclamation-circle" />{'  '}自定义图标</>}>
      this is card one
    </TabItem>
    <TabItem label="tab2">
      this is content two
    </TabItem>
  </Tabs>
  </div>
)

storiesOf('Tabs 标签页', module)
  .add('Tabs', defaultTabs)
  .add('选项卡Tabs', CardTabs)
  .add('自定义选项卡Tabs', CustomCardTabs)
import {
  FC, 
  useState, 
  createContext, 
  HTMLAttributes, 
  Children, 
  FunctionComponentElement, 
  cloneElement
} from 'react'
import classNames from 'classnames'
import {TabItemProps} from './TabItem'

type tabsType = 'line' | 'card'
type SelectCallback = (SelectIndex:number) => void

interface BaseTabs {
  /**当前激活 tab 面板的 index,默认为0 */
  defaultIndex ?: number;
  /**用户自定义选项卡样式类名 */
  className ?: string;
  /**点击 Tab 触发的函数 */
  onSelect ?: SelectCallback
  /**Tabs的样式,两种可选,默认为 line */
  type ?: tabsType
  /**默认打开的Tab */
  defaultOpenTabs ?: number[];
}

export type TabsProps = BaseTabs & HTMLAttributes<HTMLUListElement>

interface ITabsContext {
  type ?: tabsType;
  index : number;
  onSelect ?: SelectCallback;
  defaultOpenTabs ?: number[];
}

export const TabsContext = createContext<ITabsContext>({
  index:0,
})

/**
 *选项卡切换组件。 提供平级的区域将大块内容进行收纳和展现,保持界面整洁。
 * ### 引用方法
 * 
 * ~~~js
 * import { Tabs } from 'kgd'
 * ~~~
 */

const Tabs : FC<TabsProps> = (props) => {

  const {
    className,
    type,
    onSelect,
    defaultIndex,
    children,
    defaultOpenTabs
   } = props;

   const classes = classNames('kgd-tabs-nav', className, {
     [`nav-${type}`] : type
   })

   const [currentActive, setActive] = useState(defaultIndex)
   const [content, setContent] = useState()
   const [tabsOpen,setTabsopen] = useState(false)

   const handleClick = (index:number) => {
    setActive(index)
    onSelect && onSelect(index)
   }

  const passedContext : ITabsContext = {
    index : currentActive ? currentActive : 0,
    onSelect: handleClick,
    type,
    defaultOpenTabs,
  }  

  const getContent = (content : any, tabsOpen : boolean) => {
    return tabsOpen ? (
        <div className = 'kgd-tabs-content'>
          <div className = 'kgd-tab-panel'>
            {content}
          </div>
        </div> 
    ): null
  }

  const ChildrenContent = (content : any, tabsOpen : boolean) => {
    setContent(content)
    setTabsopen(tabsOpen)
  }

  const renderChildren = () => {
    return Children.map(children,(child, index) => {
      const childElement = child as FunctionComponentElement<TabItemProps>
      const { displayName } = childElement.type
      if(displayName === 'TabsItem') 
      return cloneElement(childElement, { 
        index,
        ChildrenContent
      })
      else console.error('Warning: Tabs has a child which is not a TabsItem component')
    })
  }

  return (
    <>
      <ul
        className={classes}
      >
        <TabsContext.Provider value = {passedContext}>
          {renderChildren()}
        </TabsContext.Provider>
      </ul>
      {getContent(content,tabsOpen)}
    </>
  )
}

Tabs.defaultProps = {
  defaultIndex : 0,
  defaultOpenTabs : []
}

export default Tabs;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟小胖砸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值