ahooks#useInViewport TypeScript Examples

The following examples show how to use ahooks#useInViewport. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
function index(props: IProps) {
  const { dashboardId, id, time, refreshFlag, step, type, variableConfig, isPreview, onCloneClick, onShareClick, onEditClick, onDeleteClick } = props;
  const values = _.cloneDeep(props.values);
  const ref = useRef<HTMLDivElement>(null);
  const [inViewPort] = useInViewport(ref);
  const { series, loading } = usePrometheus({
    id,
    dashboardId,
    time,
    refreshFlag,
    step,
    targets: values.targets,
    variableConfig,
    inViewPort: isPreview || inViewPort,
  });
  const tipsVisible = values.description || !_.isEmpty(values.links);
  if (_.isEmpty(values)) return null;
  // TODO: 如果 hexbin 的 colorRange 为 string 时转成成 array
  if (typeof _.get(values, 'custom.colorRange') === 'string') {
    _.set(values, 'custom.colorRange', _.split(_.get(values, 'custom.colorRange'), ','));
  }
  const subProps = {
    values,
    series,
  };
  const RendererCptMap = {
    timeseries: () => <Timeseries {...subProps} />,
    stat: () => <Stat {...subProps} />,
    table: () => <Table {...subProps} />,
    pie: () => <Pie {...subProps} />,
    hexbin: () => <Hexbin {...subProps} />,
  };

  return (
    <div className='renderer-container' ref={ref}>
      <div className='renderer-header graph-header dashboards-panels-item-drag-handle'>
        {tipsVisible ? (
          <Tooltip
            placement='rightTop'
            overlayInnerStyle={{
              width: 300,
            }}
            title={
              <div>
                <Markdown content={values.description} />
                <div>
                  {_.map(values.links, (link, i) => {
                    return (
                      <div key={i} style={{ marginTop: 8 }}>
                        <a href={link.url} target={link.targetBlank ? '_blank' : '_self'}>
                          {link.title}
                        </a>
                      </div>
                    );
                  })}
                </div>
              </div>
            }
          >
            <div className='renderer-header-desc'>
              <span className='renderer-header-info-corner-inner' />
              {values.description ? <InfoOutlined /> : <LinkOutlined />}
            </div>
          </Tooltip>
        ) : null}
        <div className='renderer-header-content'>
          {!isPreview ? (
            <Dropdown
              trigger={['click']}
              placement='bottomCenter'
              overlayStyle={{
                minWidth: '100px',
              }}
              overlay={
                <Menu>
                  {!isPreview ? (
                    <>
                      <Menu.Item onClick={onEditClick} key='0'>
                        <SettingOutlined style={{ marginRight: 8 }} />
                        编辑
                      </Menu.Item>
                      <Menu.Item onClick={onCloneClick} key='1'>
                        <CopyOutlined style={{ marginRight: 8 }} />
                        克隆
                      </Menu.Item>
                      <Menu.Item onClick={onShareClick} key='2'>
                        <ShareAltOutlined style={{ marginRight: 8 }} />
                        分享
                      </Menu.Item>
                      <Menu.Item onClick={onDeleteClick} key='3'>
                        <DeleteOutlined style={{ marginRight: 8 }} />
                        删除
                      </Menu.Item>
                    </>
                  ) : null}
                </Menu>
              }
            >
              <div className='renderer-header-title'>
                {values.name}
                <DownOutlined className='renderer-header-arrow' />
              </div>
            </Dropdown>
          ) : (
            <div className='renderer-header-title'>{values.name}</div>
          )}
        </div>
        <div className='renderer-header-loading'>{loading && <SyncOutlined spin />}</div>
      </div>
      <div className='renderer-body' style={{ height: `calc(100% - 36px)` }}>
        {RendererCptMap[type] ? RendererCptMap[type]() : `无效的图表类型 ${type}`}
      </div>
    </div>
  );
}
Example #2
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
function index(props: IProps) {
  const { dashboardId, id, time, refreshFlag, step, type, variableConfig, values, isPreview, onCloneClick, onShareClick, onEditClick, onDeleteClick } = props;
  const ref = useRef<HTMLDivElement>(null);
  const [inViewPort] = useInViewport(ref);
  const { series, loading } = usePrometheus({
    id,
    dashboardId,
    time,
    refreshFlag,
    step,
    targets: values.targets,
    variableConfig,
    inViewPort: isPreview || inViewPort,
  });
  const subProps = {
    values,
    series,
  };
  const tipsVisible = values.description || !_.isEmpty(values.links);
  if (_.isEmpty(values)) return null;
  const RendererCptMap = {
    timeseries: () => <Timeseries {...subProps} />,
    stat: () => <Stat {...subProps} />,
    table: () => <Table {...subProps} />,
    pie: () => <Pie {...subProps} />,
  };

  return (
    <div className='renderer-container' ref={ref}>
      <div className='renderer-header graph-header dashboards-panels-item-drag-handle'>
        {tipsVisible ? (
          <Tooltip
            placement='rightTop'
            overlayInnerStyle={{
              width: 300,
            }}
            title={
              <div>
                <Markdown content={values.description} />
                <div>
                  {_.map(values.links, (link) => {
                    return (
                      <div style={{ marginTop: 8 }}>
                        <a href={link.url} target={link.targetBlank ? '_blank' : '_self'}>
                          {link.title}
                        </a>
                      </div>
                    );
                  })}
                </div>
              </div>
            }
          >
            <div className='renderer-header-desc'>
              <span className='renderer-header-info-corner-inner' />
              {values.description ? <InfoOutlined /> : <LinkOutlined />}
            </div>
          </Tooltip>
        ) : null}
        <div className='renderer-header-content'>
          {!isPreview ? (
            <Dropdown
              trigger={['click']}
              placement='bottomCenter'
              overlayStyle={{
                minWidth: '100px',
              }}
              overlay={
                <Menu>
                  {!isPreview ? (
                    <>
                      <Menu.Item onClick={onEditClick} key='0'>
                        <SettingOutlined style={{ marginRight: 8 }} />
                        编辑
                      </Menu.Item>
                      <Menu.Item onClick={onCloneClick} key='1'>
                        <CopyOutlined style={{ marginRight: 8 }} />
                        克隆
                      </Menu.Item>
                      <Menu.Item onClick={onShareClick} key='2'>
                        <ShareAltOutlined style={{ marginRight: 8 }} />
                        分享
                      </Menu.Item>
                      <Menu.Item onClick={onDeleteClick} key='3'>
                        <DeleteOutlined style={{ marginRight: 8 }} />
                        删除
                      </Menu.Item>
                    </>
                  ) : null}
                </Menu>
              }
            >
              <div className='renderer-header-title'>
                {values.name}
                <DownOutlined className='renderer-header-arrow' />
              </div>
            </Dropdown>
          ) : (
            <div className='renderer-header-title'>{values.name}</div>
          )}
        </div>
        <div className='renderer-header-loading'>{loading && <SyncOutlined spin />}</div>
      </div>
      <div className='renderer-body' style={{ height: `calc(100% - 36px)` }}>
        {RendererCptMap[type] ? RendererCptMap[type]() : `无效的图表类型 ${type}`}
      </div>
    </div>
  );
}
Example #3
Source File: MessageView.tsx    From dashboard with Apache License 2.0 4 votes vote down vote up
MessageView: React.FC<MassageViewProps> = (props) => {
    const {
      timeRange,
      chatMessageList,
      chatMessageListLoading,
      chatSessionsListLoading,
      currentStaff,
      loadingMore, // 默认消息列表loading
      loadMore, // 默认消息列表 加载更多 触发函数
      currentChatSession,
      total,
      isContextView,// 是否为查看上下问消息列表
      loadEarlierData, // 查看上下文消息列表 加载更多 触发函数
      loadNearData,// 查看上下文消息列表 加载更多 触发函数
      loadingMoreEarlierData, // 查看上下文消息列表  loading
      loadingMoreNearData// 查看上下文消息列表 loading
    } = props
    const messagesEnd = useRef<HTMLDivElement>(null);
    const loadMoreTopRef = useRef<HTMLDivElement>(null);
    const loadMoreNearRef = useRef<HTMLDivElement>(null);
    const loadMoreEarlierRef = useRef<HTMLDivElement>(null);
    const previousMsg = useRef<HTMLDivElement>(null);
    const inViewPortTop = useInViewport(loadMoreTopRef)
    const inViewPortTopEarlier = useInViewport(loadMoreEarlierRef)
    const inViewPortBottom = useInViewport(loadMoreNearRef)
    const [hasLoadMore, setHasLoadMore] = useState(0)

    const scroll = (ref: any, action: string) => {
      if (ref && ref.current) {
        if(action === 'getEarlier'){
          window.scrollTo(0, ref.current.offsetTop)
        }
        if(action === 'getNear'){
          window.scrollTo(0, ref.current.offsetBottom)
        }
      }
    };

    const renderMsgDetail = (msg: NewMsg) => {
      return <div className={styles.detailContainer}>
        {
          msg.msgtype === 'text' && <div className={styles.textMsg}>
            {msg?.content_text}
          </div>
        }
        {
          msg.msgtype === 'image' && <div className={styles.imageMsg}>
            <Image src={msg?.chat_msg_content.file_url}
                   fallback={damagedImage} style={{maxWidth: 200, maxHeight: 200}}
            />
          </div>
        }
        {
          msg.msgtype !== 'text' && msg?.msgtype !== 'image' && <div className={styles.textMsg}>
            [{msgTypeMap[msg.msgtype]}消息],暂不支持预览
          </div>
        }
      </div>
    }

    const leftMsg = (msg: NewMsg, index: number) => {
      return <div className={styles.leftMsg} ref={index === 0 ? previousMsg : null}>
        <div className={styles.sendTime}>
          {msg.showTime || index === 0 ? parseTime(msg?.msgtime) : ''}
        </div>
        <div className={styles.leftMsgInfo}>
          <Image src={msg.sender_avatar} preview={false} className={styles.leftAvatar} fallback={defaultImage}/>
          {renderMsgDetail(msg)}
        </div>
      </div>
    }

    const rightMsg = (msg: NewMsg, index: number) => {
      return <div className={styles.rightMsg} ref={index === 0 ? previousMsg : null}>
        <div className={styles.sendTime}>
          {msg.showTime || index === 0 ? parseTime(msg.msgtime) : ''}
        </div>
        <div className={styles.rightMsgInfo}>
          {renderMsgDetail(msg)}
          <Image src={msg.sender_avatar} preview={false} className={styles.rightAvatar} fallback={defaultImage}/>
        </div>
      </div>
    }

    // 默认消息列表 下拉加载更早的消息
    useEffect(() => {
      if (inViewPortTop && chatMessageList.length < total && chatMessageList.length > 0) {
        console.log('loadmore')
        loadMore()
        setTimeout(() => {
          scroll(loadMoreTopRef, 'getEarlier')
        }, 1000)
      }
      setHasLoadMore(() => hasLoadMore + 1)
    }, [inViewPortTop])

    // 查看上下文消息列表 上拉加载最近的消息
    useEffect(() => {
      if (inViewPortBottom && chatMessageList.length < total && chatMessageList.length > 0 && isContextView) {
        loadNearData()
        setTimeout(() => {
          scroll(loadMoreEarlierRef, 'getEarlier')
        }, 1000)
      }
      setHasLoadMore(() => hasLoadMore + 1)
    }, [inViewPortBottom])

    // 查看上下文消息列表 下拉加载更早的消息
    useEffect(() => {
      if (inViewPortTopEarlier && chatMessageList.length < total && chatMessageList.length > 0) {
        loadEarlierData()
        setTimeout(() => {
          scroll(loadMoreNearRef, 'getNear')
        }, 1000)      }
      setHasLoadMore(() => hasLoadMore + 1)
    }, [inViewPortTopEarlier])

    useEffect(() => {
      // if (hasLoadMore === 0 && !isContextView) {
      //   scroll();
      // }
    }, [chatMessageList])

    useEffect(() => {
      setHasLoadMore(0)
    }, [currentStaff, currentChatSession])

    return (
      <div className={styles.messageViewOuter}>
        {
          timeRange && timeRange?.[0].length > 0 ? <div className={styles.timeNotion}>
            <span>{timeRange?.[0]}至{timeRange?.[1]}期间的结果</span>
          </div> : <></>
        }
        <div className={isContextView ? styles.contextViewContainer : styles.viewContainer}>
          {
            chatMessageList.length === 0 && !chatMessageListLoading && !chatSessionsListLoading &&
            <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} style={{marginTop: 100}}/>
          }
          <div className={styles.messageListBox}>
            {
              isContextView ?
                // 触发'加载更多'的Ref
                <div ref={loadMoreEarlierRef} className={styles.loadMoreSpin}>
                  <Spin spinning={loadingMoreEarlierData}/>
                </div>
                :
                <div ref={loadMoreTopRef} className={styles.loadMoreSpin}>
                  <Spin spinning={loadingMore}/>
                </div>
            }
            {/* 渲染消息列表 */}
            {
              chatMessageList?.length > 0 && chatMessageList?.map((msg, index) => {
                return msg?.sender_name === currentStaff.name ? rightMsg(msg, index) : leftMsg(msg, index)
              })
            }
          </div>
          {
            // 触发'加载更多'的Ref
            isContextView && <div ref={loadMoreNearRef} className={styles.loadMoreBottomSpin}>
              <Spin spinning={loadingMoreNearData}/>
            </div>
          }
          <div style={{clear: 'both', height: '30px', width: '100%'}} ref={messagesEnd}></div>
        </div>
      </div>
    );
  }
Example #4
Source File: Events.tsx    From dashboard with Apache License 2.0 4 votes vote down vote up
Events: React.FC<EventsProps> = (props) => {
  const {simpleRender, data, extCustomerID} = props
  const [currentType, setCurrentType] = useState('')
  const [groupData, setGroupData] = useState({} as any)
  const [eventsList, setEventsList] = useState<CustomerEvents.Item[]>([])
  const loadMoreRef = useRef<HTMLDivElement>(null);
  const [eventListLoading, setEventListLoading] = useState(true)
  const inViewPort = useInViewport(loadMoreRef);

  const getEvents = (page_size?: number, page?: number) => {
    return QueryCustomerEvents({
      ext_customer_id: extCustomerID,
      ext_staff_id: localStorage.getItem('extStaffAdminID') as string,
      page_size,
      page,
      event_type: currentType
    }).then(res => {
      console.log('QueryCustomerEventsQueryCustomerEvents', res)
      setEventsList([...eventsList].concat(res?.data?.items).filter(elem => elem !== null))
      setEventListLoading(false)
      return Promise.resolve({data: res?.data?.items || [], total: res?.data?.total_rows})
    })
  }

  const asyncFn = ({pageSize, page}: FnParams): Promise<{
    total: number;
    data: any[];
  }> => {
    return getEvents(pageSize, page).then(res => {
      return Promise.resolve({
        total: res?.total,
        data: res?.data
      })
    })
  }

  const {loadingMore, reload, loadMore, noMore} = useLoadMore<any, any>(
    asyncFn,
    {
      initPageSize: 20,
      incrementSize: 20,
    },
  );

  useEffect(() => {
    if (inViewPort && !simpleRender && eventsList.length >= 20) {
      loadMore()
    }
  }, [inViewPort])

  useEffect(() => {
    const temp = _.groupBy(simpleRender ? data : eventsList, (item) => {
      return moment(item?.created_at).format('MMM Do YYYY,dddd')
    })
    setGroupData(temp)
  }, [data, eventsList])

  const render = () => {
    return <div className={styles.groupByDay}>
      {
        Object.keys(groupData).map((key: any) => {
          return <>
            <div className={styles.day}>{key}</div>
            <div className={styles.events}>
              {
                groupData[key].map((item: any) => {
                  return <div className={styles.eventItem}>
                    <div className={styles.timeHeader}>
                      <TagsFilled />
                      <span>{moment(item?.created_at).format('h:mm')}</span>
                    </div>
                    <div className={styles.infoBottom}>
                      <div className={styles.infoBox}>
                        <span className={styles.eventType}>{customerEventType[item?.event_type]}</span>
                        <span className={styles.eventContent}>{item?.content}</span>
                      </div>
                    </div>
                  </div>
                })
              }
            </div>
          </>
        })
      }
    </div>
  }
  const onRadioChange = (e: any) => {
    setCurrentType(e.target.value)
    setEventListLoading(true)
    setEventsList([])
    reload()
  }

  return <div className={styles.events} style={{minHeight:300}}>
    <Spin spinning={eventListLoading} style={{marginTop:60}}>
      {
        simpleRender ? <div>
            {render()}
          </div>
          :
          <div>
            <Radio.Group onChange={onRadioChange} value={currentType}>
              {
                Object.keys(customerEventType).map(key => {
                  return <Radio value={key}>{customerEventType[key]}</Radio>
                })
              }
            </Radio.Group>
            <div style={{marginTop: 40}}>
              {render()}
              <Spin spinning={loadingMore}>
                <div style={{clear: 'both', height: '30px', width: '100%'}} ref={loadMoreRef}/>
              </Spin>
              <BackTop/>
              {
                noMore && eventsList?.length > 0 &&
                <div style={{display: 'flex', justifyContent: 'center', color: '#aaa'}}>没有更多信息了</div>
              }
            </div>
          </div>
      }
    </Spin>
    {
      !simpleRender && eventsList?.length === 0 && !eventListLoading && <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
    }
  </div>

}