@ant-design/icons#DownOutlined TypeScript Examples
The following examples show how to use
@ant-design/icons#DownOutlined.
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: SelectDownIcon.tsx From posthog-foss with MIT License | 6 votes |
SelectDownIcon = (props: React.HTMLAttributes<HTMLSpanElement>): JSX.Element => {
return (
<span {...props}>
<DownOutlined
style={{
paddingLeft: '0.6em',
fontSize: '90%',
opacity: 0.5,
}}
/>
</span>
)
}
Example #2
Source File: general-custom-buttons.editor.tsx From next-basics with GNU General Public License v3.0 | 6 votes |
export function DropdownBtn({
isMoreButton,
}: dropdownBtnProps): React.ReactElement {
return (
<>
<BaseButton>
{isMoreButton ? (
<EllipsisOutlined />
) : (
<>
<SettingOutlined style={{ marginRight: 5 }} /> 管理{" "}
<DownOutlined style={{ marginLeft: 5 }} />
</>
)}
</BaseButton>
</>
);
}
Example #3
Source File: FormList.tsx From condo with MIT License | 6 votes |
function ExtraDropdownActionsMenu ({ actions }) {
const actionsLine = actions.filter(identity)
const [popConfirmProps, setPopConfirmProps] = useState({ visible: false, title: null, icon: null })
function handleAction ({ key }) {
const action = actionsLine[key]
if (action.confirm) {
setPopConfirmProps({
visible: true,
onConfirm: action.action,
onCancel: () => setPopConfirmProps({ visible: false, title: null, icon: null }),
...action.confirm,
})
} else {
setPopConfirmProps({ visible: false, title: null, icon: null })
action.action()
}
}
return <Popconfirm {...popConfirmProps}>
<Dropdown overlay={<Menu onClick={handleAction}>
{actionsLine.map((action, i) => <Menu.Item key={i}>{action.label}</Menu.Item>)}
</Menu>}>
<a> ... <DownOutlined /></a>
</Dropdown>
</Popconfirm>
}
Example #4
Source File: Panel.tsx From fe-v5 with Apache License 2.0 | 6 votes |
export default function Panel(props: IProps) {
const [isActive, setIsActive] = useState<boolean>(props.isActive || true);
return (
<div
className={classnames({
'n9e-collapse-item': true,
'n9e-collapse-item-active': isActive,
'n9e-collapse-item-inner': props.isInner,
})}
>
<div
className='n9e-collapse-header'
onClick={() => {
setIsActive(!isActive);
}}
>
{isActive ? <DownOutlined className='n9e-collapse-arrow' /> : <RightOutlined className='n9e-collapse-arrow' />}
{props.header}
<div
className='n9e-collapse-extra'
onClick={(e) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
>
{props.extra}
</div>
</div>
<div
className={classnames({
'n9e-collapse-content': true,
'n9e-collapse-content-hidden': !isActive,
})}
>
<div className='n9e-collapse-content-box'>{props.children}</div>
</div>
</div>
);
}
Example #5
Source File: index.tsx From metaplex with Apache License 2.0 | 6 votes |
export function TokenButton({
mint,
onClick,
}: {
mint: PublicKey;
onClick: () => void;
}) {
const tokenMap = useTokenList().subscribedTokens;
const tokenInfo = tokenMap.filter(t => t.address == mint.toBase58())[0];
return (
<Row onClick={onClick} className={'token-button'} justify="space-between">
<Col>
<Row>
<TokenCircle
iconSize={40}
iconFile={tokenInfo?.logoURI}
style={{ marginTop: 2.5 }}
/>
<TokenName mint={mint} />
</Row>
</Col>
<Col>
<DownOutlined style={{ marginLeft: 10, fontWeight: 700 }} />
</Col>
</Row>
);
}
Example #6
Source File: TextCollapse.tsx From next-basics with GNU General Public License v3.0 | 6 votes |
export function TextCollapse(props: TextCollapseProps): React.ReactElement {
const [ellipsis, setEllipsis] = useState(true);
const { Paragraph } = Typography;
const handleClick = () => {
setEllipsis(!ellipsis);
};
return (
<div className={styles.main}>
<Paragraph
data-testid="main-text"
ellipsis={ellipsis ? { rows: props.line, expandable: false } : false}
>
{props.text}
</Paragraph>
<div className={styles.icons} onClick={handleClick} data-testid="icons">
{ellipsis ? <DownOutlined /> : <UpOutlined />}
</div>
</div>
);
}
Example #7
Source File: index.tsx From fe-v5 with Apache License 2.0 | 5 votes |
function Refresh(props: IProps, ref) {
const [intervalSeconds, setIntervalSeconds] = useState(intervalSecondsCache);
const intervalRef = useRef<NodeJS.Timeout>();
useEffect(() => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
if (intervalSeconds) {
intervalRef.current = setInterval(() => {
props.onRefresh();
}, intervalSeconds * 1000);
}
}, [intervalSeconds]);
useEffect(() => {
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);
useImperativeHandle(ref, () => ({
closeRefresh() {
setIntervalSeconds(0);
window.localStorage.setItem('refresh-interval-seconds', '0');
},
}));
return (
<div className='refresh-container'>
<Button className='refresh-btn' icon={<SyncOutlined />} onClick={props.onRefresh} />
<Dropdown
trigger={['click']}
overlay={
<Menu
onClick={(e) => {
setIntervalSeconds(_.toNumber(e.key));
window.localStorage.setItem('refresh-interval-seconds', _.toString(e.key));
}}
>
{_.map(refreshMap, (text, value) => {
return <Menu.Item key={value}>{text}</Menu.Item>;
})}
</Menu>
}
>
<Button>
{refreshMap[intervalSeconds]} <DownOutlined />
</Button>
</Dropdown>
</div>
);
}
Example #8
Source File: SearchableTree.tsx From next-basics with GNU General Public License v3.0 | 5 votes |
export function SearchableTree({
list,
defaultSelectedKeys,
icon,
field = "title",
searchPlaceholder,
onSelect,
onQChange,
customClassName,
}: SearchableTreeProps): React.ReactElement {
const [q, setQ] = useState<string>("");
const [selectedKeys, setSelectedKeys] = useState(defaultSelectedKeys);
const handleSearch = (value: string): void => {
setQ(value);
};
useEffect(() => {
onQChange?.(q);
}, [q]);
const handleClick = (selectedKeys: React.Key[], value: any): void => {
const selectedProps = value.node.props;
const select = selectedProps.id ? [selectedProps.id] : [];
setSelectedKeys(select);
onSelect?.(selectedProps);
};
const titleRender = (nodeData: Record<string, any>): React.ReactElement => {
const titleText = get(nodeData, field) as string;
let title = <span>{titleText}</span>;
if (q) {
const trimQ = q.trim();
const index = titleText.toLowerCase().indexOf(trimQ.toLowerCase());
if (index !== -1) {
const [beforeStr, matchStr, afterStr] = [
titleText.substr(0, index),
titleText.substr(index, trimQ.length),
titleText.substr(index + trimQ.length),
];
title = (
<span>
{beforeStr}
{!!matchStr && (
<span className={styles.matchedStr}>{matchStr}</span>
)}
{afterStr}
</span>
);
}
}
return <span title={nodeData.path}>{title}</span>;
};
return (
<div className={classNames(styles.container, customClassName)}>
<SearchComponent
placeholder={searchPlaceholder}
onSearch={handleSearch}
/>
{list?.length > 0 && (
<div className={styles.treeWrapper}>
<Tree
showIcon
defaultExpandAll={true}
treeData={list as any}
selectedKeys={selectedKeys}
switcherIcon={<DownOutlined />}
onSelect={handleClick}
titleRender={titleRender}
icon={icon}
blockNode={true}
></Tree>
</div>
)}
</div>
);
}
Example #9
Source File: index.tsx From ant-design-pro-V4 with MIT License | 5 votes |
Person = (props:any) => {
console.log('props:',props);
useEffect(()=>{
//调用model,更新数据
dispatch({
type:'person/fetchPersons',
payload:null
})
},[])
const {dispatch} = props
const personList = async () => {
//发起请求获取数据
// const data = await getPerson();
// return {data}
const data = props.person.persons
return {data}
}
const actionRef = useRef<ActionType>();
return (
<PageContainer>
<Card>
<ProTable<TableListItem>
columns={columns}
dataSource={props.person.persons}
// request={(params, sorter, filter) =>
// personList()
// }
rowKey="key"
pagination={{
showQuickJumper: true,
}}
search={{
// optionRender: false,
// collapsed: false,
}}
dateFormatter="string"
headerTitle="表格标题"
toolBarRender={() => [
<Button key="show">查看日志</Button>,
<Button key="out">
导出数据
<DownOutlined />
</Button>,
<Button type="primary" key="primary">
创建应用
</Button>,
]}
/>
</Card>
</PageContainer>
);
}
Example #10
Source File: index.tsx From jetlinks-ui-antd with MIT License | 5 votes |
ArrayComponents = {
CircleButton,
TextButton,
AdditionIcon: () => <PlusOutlined />,
RemoveIcon: () => <DeleteOutlined />,
MoveDownIcon: () => <DownOutlined />,
MoveUpIcon: () => <UpOutlined />
}
Example #11
Source File: index.tsx From fe-v5 with Apache License 2.0 | 5 votes |
export default function index(props: IProps) {
const { editable = true, value } = props;
return (
<Space align='baseline'>
<Dropdown
overlay={
<Menu>
{_.isEmpty(value) ? (
<div style={{ textAlign: 'center' }}>暂无数据</div>
) : (
_.map(value, (item, idx) => {
return (
<Menu.Item key={idx}>
<a href={item.url} target={item.targetBlank ? '_blank' : '_self'}>
{item.title}
</a>
</Menu.Item>
);
})
)}
</Menu>
}
>
<Button>
大盘链接 <DownOutlined />
</Button>
</Dropdown>
{editable && (
<EditOutlined
style={{ fontSize: 18 }}
className='icon'
onClick={() => {
Edit({
initialValues: value,
onOk: (newValue) => {
props.onChange(newValue);
},
});
}}
/>
)}
</Space>
);
}
Example #12
Source File: StatsCard.tsx From condo with MIT License | 5 votes |
StatsCard: React.FC<IStatsCardProps> = (props) => {
const intl = useIntl()
const extraTitle = intl.formatMessage({ id: 'component.statscard.ExtraTitle' })
const SELECTED_PERIOD: SelectedPeriod = {
calendarWeek: intl.formatMessage({ id: 'component.statscard.periodtypes.Week' }),
month: intl.formatMessage({ id: 'component.statscard.periodtypes.Month' }),
quarter: intl.formatMessage({ id: 'component.statscard.periodtypes.Quarter' }),
year: intl.formatMessage({ id: 'component.statscard.periodtypes.Year' }),
}
const { title, children, link, loading = false, onFilterChange, dependencyArray } = props
const [selectedPeriod, setSelectedPeriod] = useState<string>(Object.keys(SELECTED_PERIOD)[0])
const updateDependencies = [selectedPeriod, ...dependencyArray]
useEffect(() => {
onFilterChange(selectedPeriod)
}, updateDependencies)
const menuClick = useCallback(({ key }) => { setSelectedPeriod(key)}, [])
const linkClick = useCallback(() => { Router.push(link) }, [link])
const menuOverlay = (
<Menu onClick={menuClick} disabled={loading}>
{
Object.keys(SELECTED_PERIOD).map((period) => (
<Menu.Item key={period}>{SELECTED_PERIOD[period]}</Menu.Item>
))
}
</Menu>
)
const cardTitle = (
<Space css={cardTitleCss}>
{title}
<Dropdown overlay={menuOverlay} >
<span style={DROPDOWN_TEXT_STYLE}>{SELECTED_PERIOD[selectedPeriod]} <DownOutlined /></span>
</Dropdown>
</Space>
)
const cardExtra = (
<Button style={CARD_EXTRA_STYLE} type={'inlineLink'} onClick={linkClick}>
{extraTitle}{<RightOutlined />}
</Button>
)
return (
<Row gutter={STATS_CARD_ROW_GUTTER} align={'middle'}>
<Col span={24}>
<Card
title={cardTitle}
bordered={false}
headStyle={CARD_HEAD_STYLE}
extra={cardExtra}
>
{loading ? <Skeleton active round paragraph={{ rows: 1 }} /> : children}
</Card>
</Col>
</Row>
)
}
Example #13
Source File: index.tsx From amiya with MIT License | 5 votes |
export default function AyCard(props: AyCardProps) {
const { collapsible, onCollapse, defaultCollapsed, children, title, extra, collapsePosition = 'extra' } = props
const [collapsed, setCollapsed] = useState<boolean>(props.collapsed || defaultCollapsed || false)
const params = { ...props }
delete params.collapsible
delete params.defaultCollapsed
delete params.collapsePosition
delete params.collapsed
delete params.onCollapse
useEffect(() => {
setCollapsed(props.collapsed || false)
}, [props.collapsed])
const toggleCollapsed = () => {
setCollapsed(!collapsed)
if (onCollapse) {
onCollapse(!collapsed)
}
}
const icon = collapsible ? (
<DownOutlined className={`ay-card-collapsible-icon ${collapsePosition}`} onClick={toggleCollapsed} />
) : null
return (
<Card
{...params}
className={`ay-card ${collapsed ? 'collapsed' : ''}`}
title={
title ? (
<>
{collapsePosition === 'title' ? icon : null}
{title}
</>
) : null
}
extra={
title ? (
<>
{extra}
{collapsePosition === 'extra' ? icon : null}
</>
) : null
}
>
{children}
</Card>
)
}
Example #14
Source File: property.tsx From LogicFlow with Apache License 2.0 | 5 votes |
// @ts-ignore
export default function PropertyPanel(nodeData, updateproperty, hidePropertyPanel) {
const getApproveList = () => {
const approveUserOption: JSX.Element[] = []
approveUser.forEach((item: IApproveUser) => {
approveUserOption.push(<Select.Option value={item.value}>{item.label}</Select.Option>);
});
const approveSelect = <Form.Item className="form-property" label="审核节点类型" name="approveType">
<Select>
{approveUserOption}
</Select>
</Form.Item>
return approveSelect;
}
const getApiUrl = () => {
const Api = <Form.Item label="API" name="api">
<Input />
</Form.Item>
return Api;
}
const onFormLayoutChange = (value: any, all: any) => {
approveUser.forEach(item => {
if (item.value === value.approveType) {
value['approveTypeLabel'] = item.label;
}
})
updateproperty(nodeData.id, value,);
}
return (
<div>
<h2>属性面板</h2>
<Form
key={nodeData.id}
layout="inline"
initialValues={nodeData.properties}
onValuesChange={onFormLayoutChange}
>
<span className="form-property">类型:<span>{nodeData.type}</span></span>
<span className="form-property">文案:<span>{nodeData.text?.value}</span></span>
{nodeData.type==="approver" ? getApproveList() : ''}
{nodeData.type === "jugement" ? getApiUrl() : ''}
</Form>
<div>
<h3>......</h3>
<h3>业务属性可根据需要进行自定义扩展</h3>
</div>
<div className="property-panel-footer">
<Button
className="property-panel-footer-hide"
type="primary"
icon={<DownOutlined/>}
onClick={hidePropertyPanel}>
收起
</Button>
</div>
</div>
)
}
Example #15
Source File: TeamMembers.tsx From posthog-foss with MIT License | 5 votes |
function LevelComponent(member: FusedTeamMemberType): JSX.Element | null {
const { user } = useValues(userLogic)
const { currentTeam } = useValues(teamLogic)
const { changeUserAccessLevel } = useActions(teamMembersLogic)
const myMembershipLevel = currentTeam ? currentTeam.effective_membership_level : null
if (!user) {
return null
}
function generateHandleClick(listLevel: TeamMembershipLevel): (event: React.MouseEvent) => void {
return function handleClick(event: React.MouseEvent) {
event.preventDefault()
changeUserAccessLevel(member.user, listLevel)
}
}
const isImplicit = member.organization_level >= OrganizationMembershipLevel.Admin
const levelName = membershipLevelToName.get(member.level) ?? `unknown (${member.level})`
const levelButton = (
<Button
data-attr="change-membership-level"
icon={member.level === OrganizationMembershipLevel.Owner ? <CrownFilled /> : undefined}
// Org admins have implicit access anyway, so it doesn't make sense to edit them
disabled={isImplicit}
>
{levelName}
</Button>
)
const allowedLevels = teamMembershipLevelIntegers.filter(
(listLevel) => !getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, listLevel)
)
const disallowedReason = isImplicit
? `This user is a member of the project implicitly due to being an organization ${levelName}.`
: getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, allowedLevels)
return disallowedReason ? (
<Tooltip title={disallowedReason}>{levelButton}</Tooltip>
) : (
<Dropdown
overlay={
<Menu>
{allowedLevels.map((listLevel) => (
<Menu.Item key={`${member.user.uuid}-level-${listLevel}`}>
<a href="#" onClick={generateHandleClick(listLevel)} data-test-level={listLevel}>
{listLevel > member.level ? (
<>
<UpOutlined style={{ marginRight: '0.5rem' }} />
Upgrade to project {membershipLevelToName.get(listLevel)}
</>
) : (
<>
<DownOutlined style={{ marginRight: '0.5rem' }} />
Downgrade to project {membershipLevelToName.get(listLevel)}
</>
)}
</a>
</Menu.Item>
))}
</Menu>
}
>
{levelButton}
</Dropdown>
)
}
Example #16
Source File: index.tsx From fe-v5 with Apache License 2.0 | 4 votes |
index = (_props: any) => {
const { t, i18n } = useTranslation();
const searchRef = useRef<Input>(null);
const [query, setQuery] = useState('');
const { curBusiItem } = useSelector<RootState, CommonStoreState>((state) => state.common);
const busiId = curBusiItem.id;
const [selectedIds, setSelectedIds] = useState([] as any[]);
const { tableProps, refresh } = useAntdTable<any, any>((options) => getTableData(options, busiId, query), { refreshDeps: [busiId, query] });
function handleTagClick(tag: string) {
if (!_.includes(query, tag)) {
const newQuery = query ? `${query} ${tag}` : tag;
setQuery(newQuery);
searchRef.current?.setValue(newQuery);
}
}
function handleDelBtnClick(id: number) {
if (busiId) {
request(`${api.tasktpl(busiId)}/${id}`, {
method: 'DELETE',
}).then(() => {
message.success(t('msg.delete.success'));
refresh();
});
}
}
function handleBatchBindTags() {
if (!_.isEmpty(selectedIds)) {
BindTags({
language: i18n.language,
selectedIds,
busiId,
onOk: () => {
refresh();
},
});
}
}
function handleBatchUnBindTags() {
if (!_.isEmpty(selectedIds)) {
let uniqueTags = [] as any[];
_.each(tableProps.dataSource, (item) => {
const tags = item.tags;
uniqueTags = _.union(uniqueTags, tags);
});
UnBindTags({
language: i18n.language,
selectedIds,
uniqueTags,
busiId,
onOk: () => {
refresh();
},
});
}
}
const columns: ColumnProps<Tpl>[] = [
{
title: 'ID',
dataIndex: 'id',
},
{
title: t('tpl.title'),
dataIndex: 'title',
render: (text, record) => {
return <Link to={{ pathname: `/job-tpls/${record.id}/detail` }}>{text}</Link>;
},
},
{
title: t('tpl.tags'),
dataIndex: 'tags',
render: (text) => {
return _.map(text, (item) => (
<Tag color='blue' key={item} onClick={() => handleTagClick(item)}>
{item}
</Tag>
));
},
},
{
title: t('tpl.creator'),
dataIndex: 'create_by',
width: 100,
},
{
title: t('tpl.last_updated'),
dataIndex: 'update_at',
width: 160,
render: (text) => {
return moment.unix(text).format('YYYY-MM-DD HH:mm:ss');
},
},
{
title: t('table.operations'),
width: 220,
render: (_text, record) => {
return (
<span>
<Link to={{ pathname: `/job-tpls/add/task`, search: `tpl=${record.id}` }}>{t('task.create')}</Link>
<Divider type='vertical' />
<Link to={{ pathname: `/job-tpls/${record.id}/modify` }}>{t('table.modify')}</Link>
<Divider type='vertical' />
<Link to={{ pathname: `/job-tpls/${record.id}/clone` }}>{t('table.clone')}</Link>
<Divider type='vertical' />
<Popconfirm
title={<div style={{ width: 100 }}>{t('table.delete.sure')}</div>}
onConfirm={() => {
handleDelBtnClick(record.id);
}}
>
<a style={{ color: 'red' }}>{t('table.delete')}</a>
</Popconfirm>
</span>
);
},
},
];
return (
<PageLayout
hideCluster
title={
<>
<CodeOutlined />
{t('自愈脚本')}
</>
}
>
<div style={{ display: 'flex' }}>
<LeftTree></LeftTree>
{busiId ? (
<div style={{ flex: 1, padding: 20 }}>
<Row>
<Col span={14} className='mb10'>
<Input
style={{ width: 200 }}
ref={searchRef}
prefix={<SearchOutlined />}
defaultValue={query}
onPressEnter={(e) => {
setQuery(e.currentTarget.value);
}}
placeholder='搜索标题、标签'
/>
</Col>
<Col span={10} className='textAlignRight'>
<Link to={{ pathname: `/job-tpls/add` }}>
<Button icon={<PlusOutlined />} style={{ marginRight: 10 }} type='primary' ghost>
{t('tpl.create')}
</Button>
</Link>
<Dropdown
overlay={
<Menu>
<Menu.Item>
<Button
type='link'
disabled={selectedIds.length === 0}
onClick={() => {
handleBatchBindTags();
}}
>
{t('tpl.tag.bind')}
</Button>
</Menu.Item>
<Menu.Item>
<Button
type='link'
disabled={selectedIds.length === 0}
onClick={() => {
handleBatchUnBindTags();
}}
>
{t('tpl.tag.unbind')}
</Button>
</Menu.Item>
</Menu>
}
>
<Button icon={<DownOutlined />}>{t('table.batch.operations')}</Button>
</Dropdown>
</Col>
</Row>
<Table
rowKey='id'
columns={columns}
{...(tableProps as any)}
rowSelection={{
selectedRowKeys: selectedIds,
onChange: (selectedRowKeys) => {
setSelectedIds(selectedRowKeys);
},
}}
pagination={
{
...tableProps.pagination,
showSizeChanger: true,
pageSizeOptions: ['10', '50', '100', '500', '1000'],
showTotal: (total) => {
return i18n.language == 'en' ? `Total ${total} items` : `共 ${total} 条`;
},
} as any
}
/>
</div>
) : (
<BlankBusinessPlaceholder text='自愈脚本' />
)}
</div>
</PageLayout>
);
}
Example #17
Source File: TicketChartView.tsx From condo with MIT License | 4 votes |
TicketChartView: React.FC<ITicketAnalyticsPageChartProps> = (props) => {
const {
children,
data,
viewMode,
loading = false,
onChartReady,
chartConfig,
mapperInstance,
mainGroup = 'ticket',
} = props
const intl = useIntl()
const NoData = intl.formatMessage({ id: 'NoData' })
const LoadMoreTitle = intl.formatMessage(
{ id: 'pages.condo.analytics.TicketAnalyticsPage.TicketChartView.LoadMoreTitle' },
{ entity: intl.formatMessage({ id: `component.TicketWarningModal.Entity.${mainGroup}` }) }
)
let legend = [], tooltip = null, color = CHART_COLOR_SET
const [chartReadyCounter, setChartReadyCounter] = useState<number>(0)
// Start from 1 because used as multiplier with TICKET_CHART_PAGE_SIZE
const [chartPage, setChartPage] = useState<number>(1)
// Store pie chart refs because we need to access api for every chart component
const chartRefs = useRef([])
// Cache series result for client side paging
const seriesRef = useRef<EchartsSeries[]>([])
const seriesCacheRef = useRef<EchartsSeries[]>([])
const axisDataRef = useRef<ChartConfigResult['axisData'] | null>(null)
if (data !== null) {
const mapperResult = mapperInstance.getChartConfig(viewMode, data)
seriesCacheRef.current = mapperResult.series
const pageSize = TICKET_CHART_PAGE_SIZE * chartPage
seriesRef.current = mapperResult.series.slice(0, pageSize)
// If component used as report widget, we should use page separation. Otherwise we load all data (for pdf page)
legend = mapperResult.legend
axisDataRef.current = mapperResult.axisData
tooltip = mapperResult.tooltip
if (mapperResult.color) {
color = mapperResult.color
}
}
useEffect(() => {
setChartPage(1)
}, [viewMode, mainGroup])
useEffect(() => {
// Clean chart refs if input data was changed
if (viewMode === 'pie') {
if (seriesRef.current.length !== chartRefs.current.length) {
// It means that filters were changed & total chart count may be changed too, so just reset refs & page
setChartReadyCounter(0)
setChartPage(1)
}
chartRefs.current = chartRefs.current.slice(0, seriesRef.current.length)
}
}, [data, viewMode])
// Way to await moment when all pie chart instance were rendered (needed for client side pdf generation)
useLayoutEffect(() => {
if (viewMode === 'pie' && onChartReady !== undefined) {
if (seriesRef.current.length !== 0 && seriesCacheRef.current.length !== 0 && seriesRef.current.length === seriesCacheRef.current.length) {
onChartReady()
return
}
if (chartReadyCounter < seriesRef.current.length) {
let animationFrameId
const nextAnimationFrame = () => {
setChartPage(pieChartPage => pieChartPage + 1)
animationFrameId = requestAnimationFrame(nextAnimationFrame)
}
animationFrameId = requestAnimationFrame(nextAnimationFrame)
return () => cancelAnimationFrame(animationFrameId)
}
}
}, [chartReadyCounter])
const loadMore = useCallback(() => { setChartPage(chartPage + 1) }, [chartPage])
if (data === null || loading) {
return <Skeleton loading={loading} active paragraph={{ rows: 12 }} />
}
const { animationEnabled, chartOptions } = chartConfig
const isEmptyDataSet = data.every(ticketStatus => ticketStatus.count === 0)
if (viewMode !== 'pie') {
const pieChartDataStep = chartPage * TICKET_CHART_PAGE_SIZE
const axisData = axisDataRef.current
const hasMore = axisData !== null && pieChartDataStep < get(axisData, 'yAxis.data.length', 0)
const barChartPagedRenderEnabled = animationEnabled && hasMore && viewMode === 'bar'
if (barChartPagedRenderEnabled) {
axisData['yAxis']['data'] = get(axisData, 'yAxis.data', []).slice(-pieChartDataStep)
seriesCacheRef.current.forEach(series => {
series.data = series.data.slice(-pieChartDataStep)
})
}
const { opts, option } = getChartOptions({
legend,
axisData,
tooltip,
series: seriesCacheRef.current,
chartOptions,
viewMode,
animationEnabled,
color,
})
const chartHeight = get(opts, 'height', 'auto')
const chartStyle = {
height: chartHeight,
}
return <ChartViewContainer>
{isEmptyDataSet ? (
<>
<BasicEmptyListView>
<Typography.Text>{NoData}</Typography.Text>
</BasicEmptyListView>
{children}
</>
) : (
<>
<ReactECharts
opts={opts}
onChartReady={onChartReady}
notMerge
style={{ ...chartStyle }}
option={option}/>
{barChartPagedRenderEnabled ? (
<Button style={{ width: '100%', marginTop: 16 }} type={'ghost'} onClick={loadMore}>
{LoadMoreTitle} <DownOutlined />
</Button>
) : null}
{children}
</>
)}
</ChartViewContainer>
}
const hasMore = chartPage * TICKET_CHART_PAGE_SIZE <= seriesRef.current.length
let infiniteScrollContainerHeight = seriesRef.current.length > 2 ? '660px' : '340px'
// onChartReadyCallback is used only for pdf generation page to make sure that chart component was rendered at DOM
if (onChartReady !== undefined) {
infiniteScrollContainerHeight = '100%'
}
return <ChartViewContainer>
{isEmptyDataSet ? (
<>
<BasicEmptyListView>
<Typography.Text>{NoData}</Typography.Text>
</BasicEmptyListView>
{children}
</>
) : (
<>
<ReactECharts
onEvents={{
legendselectchanged: function (params) {
chartRefs.current.forEach(chartRef => {
const chartInstance = chartRef.getEchartsInstance()
chartInstance._api.dispatchAction({
type: 'legendToggleSelect',
name: params.name,
})
})
},
}}
opts={{ renderer: 'svg' }}
notMerge
option={
getChartOptions({
legend,
color,
series: [{ ...seriesRef.current[0], top: -1000, left: -1000, labelLayout: {}, label: { show: false } }],
viewMode: 'pie',
animationEnabled,
chartOptions: { renderer: 'svg' },
showTitle: false,
}).option
}
style={{ height: 40, overflow: 'hidden' }}
/>
<ScrollContainer height={infiniteScrollContainerHeight}>
<InfiniteScroll
initialLoad={false}
loadMore={loadMore}
hasMore={hasMore}
useWindow={false}>
<List
grid={{ gutter: 24, xs: 1, sm: 1, md: 1, lg: 1, xl: 2, xxl: 2 }}
dataSource={seriesRef.current}
renderItem={(chartSeries, index) => {
const { option, opts } = getChartOptions({
series: [chartSeries],
legend,
viewMode,
chartOptions,
animationEnabled,
color,
})
return (
<List.Item key={`pie-${index}`} style={{ width: 620 }}>
<ReactECharts
ref={element => chartRefs.current[index] = element}
opts={opts}
onChartReady={() => setChartReadyCounter(chartReadyCounter + 1)}
notMerge
style={{
border: '1px solid',
borderColor: colors.lightGrey[6],
borderRadius: 8,
}}
option={option}
/>
</List.Item>
)
}}
style={{ paddingRight: 20, width: '100%' }}
/>
</InfiniteScroll>
</ScrollContainer>
{children}
</>
)}
</ChartViewContainer>
}
Example #18
Source File: VerificationPanel.tsx From posthog-foss with MIT License | 4 votes |
export function VerificationPanel(): JSX.Element {
const { loadCurrentTeam } = useActions(teamLogic)
const { currentTeam } = useValues(teamLogic)
const { setVerify, completeOnboarding } = useActions(ingestionLogic)
const { index, totalSteps } = useValues(ingestionLogic)
const [isPopConfirmShowing, setPopConfirmShowing] = useState(false)
const [isHelpMenuShowing, setHelpMenuShowing] = useState(false)
const { showInviteModal } = useActions(inviteLogic)
useInterval(() => {
if (!currentTeam?.ingested_event && !isPopConfirmShowing && !isHelpMenuShowing) {
loadCurrentTeam()
}
}, 2000)
function HelperButtonRow(): JSX.Element {
function HelpButton(): JSX.Element {
const menu = (
<Menu selectable>
<Menu.Item key="0" data-attr="ingestion-help-item-docs">
<a href="https://posthog.com/docs/integrate/ingest-live-data" target="_blank">
<Button type="link">
<ReadOutlined />
Read the ingestion docs
</Button>
</a>
</Menu.Item>
<Menu.Item key="1" data-attr="ingestion-help-item-invite">
<Button type="link" onClick={showInviteModal}>
<UserAddOutlined />
Invite team member
</Button>
</Menu.Item>
<Menu.Item key="2" data-attr="ingestion-help-item-slack">
<a href="https://posthog.com/slack?s=app" target="_blank">
<Button type="link">
<SlackSquareOutlined />
Ask us in Slack
</Button>
</a>
</Menu.Item>
</Menu>
)
return (
<Dropdown
overlay={menu}
trigger={['click']}
visible={isHelpMenuShowing}
onVisibleChange={(v) => {
setHelpMenuShowing(v)
}}
>
<Button type="primary" data-attr="ingestion-help-button" onClick={() => setHelpMenuShowing(true)}>
Need help? <DownOutlined />
</Button>
</Dropdown>
)
}
const popoverTitle = (
<Space direction="vertical">
<Text strong>Are you sure you want to continue without data?</Text>
<Text>You won't be able to conduct analysis or use most tools without event data.</Text>
<Text>For the best experience, we recommend adding event data before continuing.</Text>
</Space>
)
return (
<Space style={{ float: 'right' }}>
<Popconfirm
title={popoverTitle}
okText="Yes, I know what I'm doing."
okType="danger"
cancelText="No, go back."
onVisibleChange={(v) => {
setPopConfirmShowing(v)
}}
onCancel={() => {
setPopConfirmShowing(false)
}}
visible={isPopConfirmShowing}
onConfirm={completeOnboarding}
cancelButtonProps={{ type: 'primary' }}
>
<Button
type="dashed"
data-attr="ingestion-continue-anyway"
onClick={() => {
setPopConfirmShowing(true)
}}
>
Continue without verifying
</Button>
</Popconfirm>
<HelpButton />
</Space>
)
}
return (
<CardContainer index={index} totalSteps={totalSteps} onBack={() => setVerify(false)}>
{!currentTeam?.ingested_event ? (
<>
<Row className="flex-center">
<Spinner style={{ marginRight: 4 }} />
<h2 className="ml-3" style={{ marginBottom: 0, color: 'var(--primary-alt)' }}>
Listening for events...
</h2>
</Row>
<p className="prompt-text mt-05">
Once you have integrated the snippet and sent an event, we will verify it was properly received
and continue.
</p>
<HelperButtonRow />
</>
) : (
<>
<h2>Successfully sent events!</h2>
<p className="prompt-text">
You will now be able to explore PostHog and take advantage of all its features to understand
your users.
</p>
<Button
data-attr="wizard-complete-button"
type="primary"
style={{ float: 'right' }}
onClick={completeOnboarding}
>
Complete
</Button>
</>
)}
</CardContainer>
)
}
Example #19
Source File: PageTable.tsx From fe-v5 with Apache License 2.0 | 4 votes |
PageTable: React.FC<Props> = ({ bgid }) => {
const [severity, setSeverity] = useState<number>();
const [clusters, setClusters] = useState<string[]>([]);
const { t, i18n } = useTranslation();
const history = useHistory();
const [modalType, setModalType] = useState<ModalStatus>(ModalStatus.None);
const [selectRowKeys, setSelectRowKeys] = useState<React.Key[]>([]);
const [selectedRows, setSelectedRows] = useState<strategyItem[]>([]);
const [exportData, setExportData] = useState<string>('');
const { curBusiItem } = useSelector<RootState, CommonStoreState>((state) => state.common);
const [query, setQuery] = useState<string>('');
const [isModalVisible, setisModalVisible] = useState<boolean>(false);
const [currentStrategyDataAll, setCurrentStrategyDataAll] = useState([]);
const [currentStrategyData, setCurrentStrategyData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (bgid) {
getAlertRules();
}
}, [bgid, severity]);
useEffect(() => {
filterData();
}, [query, clusters, currentStrategyDataAll]);
const getAlertRules = async () => {
if (!bgid) {
return;
}
setLoading(true);
const { success, dat } = await getStrategyGroupSubList({ id: bgid });
if (success) {
setCurrentStrategyDataAll(dat.filter((item) => !severity || item.severity === severity) || []);
setLoading(false);
}
};
const filterData = () => {
const data = JSON.parse(JSON.stringify(currentStrategyDataAll));
const res = data.filter((item) => {
const lowerCaseQuery = query.toLowerCase();
return (
(item.name.toLowerCase().indexOf(lowerCaseQuery) > -1 || item.append_tags.join(' ').toLowerCase().indexOf(lowerCaseQuery) > -1) &&
((clusters && clusters?.indexOf(item.cluster) > -1) || clusters?.length === 0)
);
});
setCurrentStrategyData(res || []);
};
const goToAddWarningStrategy = () => {
curBusiItem?.id && history.push(`/alert-rules/add/${curBusiItem.id}`);
};
const handleClickEdit = (id, isClone = false) => {
curBusiItem?.id && history.push(`/alert-rules/edit/${id}${isClone ? '?mode=clone' : ''}`);
};
const refreshList = () => {
getAlertRules();
};
const columns: ColumnType<strategyItem>[] = [
{
title: t('集群'),
dataIndex: 'cluster',
render: (data) => {
return <div>{data}</div>;
},
},
{
title: t('级别'),
dataIndex: 'severity',
render: (data) => {
return <Tag color={priorityColor[data - 1]}>S{data}</Tag>;
},
},
{
title: t('名称'),
dataIndex: 'name',
render: (data, record) => {
return (
<div
className='table-active-text'
onClick={() => {
handleClickEdit(record.id);
}}
>
{data}
</div>
);
},
},
{
title: t('告警接收者'),
dataIndex: 'notify_groups_obj',
width: 100,
render: (data, record) => {
return (
(data.length &&
data.map(
(
user: {
nickname: string;
username: string;
} & { name: string },
index: number,
) => {
return <ColorTag text={user.nickname || user.username || user.name} key={index}></ColorTag>;
},
)) || <div></div>
);
},
},
{
title: t('附加标签'),
dataIndex: 'append_tags',
render: (data) => {
const array = data || [];
return (
(array.length &&
array.map((tag: string, index: number) => {
return <ColorTag text={tag} key={index}></ColorTag>;
})) || <div></div>
);
},
},
{
title: t('更新时间'),
dataIndex: 'update_at',
width: 120,
render: (text: string) => dayjs(Number(text) * 1000).format('YYYY-MM-DD HH:mm:ss'),
},
{
title: t('启用'),
dataIndex: 'disabled',
render: (disabled, record) => (
<Switch
checked={disabled === strategyStatus.Enable}
size='small'
onChange={() => {
const { id, disabled } = record;
updateAlertRules(
{
ids: [id],
fields: {
disabled: !disabled ? 1 : 0,
},
},
curBusiItem.id,
).then(() => {
refreshList();
});
}}
/>
),
},
{
title: t('操作'),
dataIndex: 'operator',
width: 100,
render: (data, record) => {
return (
<div className='table-operator-area'>
<div
className='table-operator-area-normal'
onClick={() => {
handleClickEdit(record.id, true);
}}
>
{t('克隆')}
</div>
<div
className='table-operator-area-warning'
onClick={() => {
confirm({
title: t('是否删除该告警规则?'),
onOk: () => {
deleteStrategy([record.id], curBusiItem.id).then(() => {
message.success(t('删除成功'));
refreshList();
});
},
onCancel() {},
});
}}
>
{t('删除')}
</div>
</div>
);
},
},
];
const toOneArr = (arr, res, name) => {
arr.forEach((ele) => {
if (Array.isArray(ele)) {
toOneArr(ele, res, name);
} else {
res.push(ele[name]);
}
});
};
const openModel = (title, item) => {
if (selectRowKeys.length == 0) {
message.warning(t('请先选择策略'));
return;
}
setisModalVisible(true);
};
const menu = useMemo(() => {
return (
<ul className='ant-dropdown-menu'>
<li className='ant-dropdown-menu-item' onClick={() => setModalType(ModalStatus.BuiltIn)}>
<span>{t('导入告警规则')}</span>
</li>
<li
className='ant-dropdown-menu-item'
onClick={() => {
if (selectedRows.length) {
const exportData = selectedRows.map((item) => {
return { ...item, ...exportIgnoreAttrsObj };
});
setExportData(JSON.stringify(exportData, null, 2));
setModalType(ModalStatus.Export);
} else {
message.warning(t('未选择任何规则'));
}
}}
>
<span>{t('导出告警规则')}</span>
</li>
<li
className='ant-dropdown-menu-item'
onClick={() => {
if (selectRowKeys.length) {
confirm({
title: t('是否批量删除告警规则?'),
onOk: () => {
deleteStrategy(selectRowKeys as number[], curBusiItem?.id).then(() => {
message.success(t('删除成功'));
refreshList();
});
},
onCancel() {},
});
} else {
message.warning(t('未选择任何规则'));
}
}}
>
<span>{t('批量删除规则')}</span>
</li>
<li
className='ant-dropdown-menu-item'
onClick={() => {
openModel(t('批量更新规则'), 1);
}}
>
<span>{t('批量更新规则')}</span>
</li>
</ul>
);
}, [selectRowKeys, t]);
const handleImportStrategy = async (data) => {
const { dat } = await addOrEditStrategy(data, curBusiItem.id, 'Post');
return dat || {};
};
const editModalFinish = async (isOk, fieldsData?) => {
if (isOk) {
const res = await updateAlertRules(
{
ids: selectRowKeys,
fields: fieldsData,
},
curBusiItem.id,
);
if (!res.err) {
message.success('修改成功!');
refreshList();
setisModalVisible(false);
} else {
message.error(res.err);
}
} else {
setisModalVisible(false);
}
};
return (
<div className='strategy-table-content'>
<div className='strategy-table-search table-handle'>
<div className='strategy-table-search-left'>
<RefreshIcon
className='strategy-table-search-left-refresh'
onClick={() => {
refreshList();
}}
/>
<ColumnSelect noLeftPadding noRightPadding={false} onSeverityChange={(e) => setSeverity(e)} onClusterChange={(e) => setClusters(e)} />
<SearchInput className={'searchInput'} placeholder={t('搜索名称或标签')} onSearch={setQuery} allowClear />
</div>
<div className='strategy-table-search-right'>
<Button type='primary' onClick={goToAddWarningStrategy} className='strategy-table-search-right-create' ghost>
{t('新增告警规则')}
</Button>
<div className={'table-more-options'}>
<Dropdown overlay={menu} trigger={['click']}>
<Button onClick={(e) => e.stopPropagation()}>
{t('更多操作')}
<DownOutlined
style={{
marginLeft: 2,
}}
/>
</Button>
</Dropdown>
</div>
</div>
</div>
<Table
rowKey='id'
// sticky
pagination={{
total: currentStrategyData.length,
showQuickJumper: true,
showSizeChanger: true,
showTotal: (total) => {
return `共 ${total} 条数据`;
},
pageSizeOptions: pageSizeOptionsDefault,
defaultPageSize: 30,
}}
loading={loading}
dataSource={currentStrategyData}
rowSelection={{
selectedRowKeys: selectedRows.map((item) => item.id),
onChange: (selectedRowKeys: React.Key[], selectedRows: strategyItem[]) => {
setSelectRowKeys(selectedRowKeys);
setSelectedRows(selectedRows);
},
}}
columns={columns}
/>
<ImportAndDownloadModal
bgid={bgid}
status={modalType}
fetchBuiltinFunc={getBuiltinAlerts}
submitBuiltinFunc={createBuiltinAlerts}
onClose={() => {
setModalType(ModalStatus.None);
}}
onSuccess={() => {
getAlertRules();
}}
onSubmit={handleImportStrategy}
label='告警规则'
title={
ModalStatus.Export === modalType ? (
'告警规则'
) : (
<Tabs defaultActiveKey={ModalStatus.BuiltIn} onChange={(e: ModalStatus) => setModalType(e)} className='custom-import-alert-title'>
<TabPane tab=' 导入内置告警规则' key={ModalStatus.BuiltIn}></TabPane>
<TabPane tab='导入告警规则JSON' key={ModalStatus.Import}></TabPane>
</Tabs>
)
}
exportData={exportData}
/>
{isModalVisible && <EditModal isModalVisible={isModalVisible} editModalFinish={editModalFinish} />}
</div>
);
}
Example #20
Source File: Telemetry.tsx From nanolooker with MIT License | 4 votes |
Telemetry: React.FC = () => {
const { t } = useTranslation();
const [currentPercentile, setCurrentPercentile] = React.useState(
Percentiles.P95,
);
const {
telemetry,
versions,
status: { nodeCount, bandwidthCapGroups, date } = {},
isLoading: isTelemetryLoading,
} = useTelemetry();
const [
formattedMedianBandwidthCap,
setFormattedMedianBandwidthCap,
] = React.useState(formatBytes(0));
const [
unlimitedBandwidthCapCount,
setUnlimitedBandwidthCapCount,
] = React.useState<number>();
const [
limitedBandwidthCapCount,
setLimitedBandwidthCapCount,
] = React.useState<number>();
const [limitedBandwidthCap, setLimitedBandwidthCap] = React.useState(
formatBytes(0),
);
React.useEffect(() => {
if (!telemetry[currentPercentile]) return;
setFormattedMedianBandwidthCap(
formatBytes(telemetry[currentPercentile].bandwidthCap),
);
}, [telemetry, currentPercentile]);
const onPercentileClick = ({ key }: any) => {
setCurrentPercentile(key);
};
React.useEffect(() => {
if (!bandwidthCapGroups) return;
setUnlimitedBandwidthCapCount(bandwidthCapGroups[0].count);
setLimitedBandwidthCapCount(bandwidthCapGroups[1]?.count);
setLimitedBandwidthCap(formatBytes(bandwidthCapGroups[1]?.bandwidthCap));
}, [bandwidthCapGroups]);
return (
<Row gutter={12}>
<Col xs={24} md={16}>
<div
style={{
display: "flex",
alignItems: "baseline",
marginBottom: "12px",
}}
>
<Title level={3} style={{ margin: 0 }}>
{t("pages.status.telemetry")}
</Title>
<div style={{ marginLeft: "12px" }}>
<Dropdown
overlay={
<Menu onClick={onPercentileClick}>
{Object.values(Percentiles).map(percentile => (
<Menu.Item key={percentile}>{percentile}</Menu.Item>
))}
</Menu>
}
>
<Button>
{currentPercentile} <DownOutlined />
</Button>
</Dropdown>
</div>
<Tooltip placement="right" title={t("tooltips.telemetry")}>
<QuestionCircle />
</Tooltip>
</div>
<Card
size="small"
bordered={false}
className="detail-layout"
style={{ marginBottom: "12px" }}
>
<div style={{ marginBottom: "12px", fontSize: "12px" }}>
{date ? (
<>
{t("common.executionTimeAgo")}{" "}
<TimeAgo
locale={i18next.language}
datetime={date}
live={false}
style={{ fontWeight: "bold" }}
/>
</>
) : null}
{nodeCount ? (
<>
<br />
<Trans i18nKey="pages.status.nodeCount">
<strong>{{ nodeCount }}</strong>
</Trans>
</>
) : null}
{unlimitedBandwidthCapCount ? (
<>
<br />
<Trans i18nKey="pages.status.unlimitedBandwidthCap">
<strong>{{ unlimitedBandwidthCapCount }}</strong>
</Trans>
</>
) : null}
{limitedBandwidthCapCount && limitedBandwidthCap.value ? (
<>
{" "}
<Trans i18nKey="pages.status.limitedBandwidthCap">
<strong>{{ limitedBandwidthCapCount }}</strong>
<strong>
{{
limitedBandwidthCap: `${limitedBandwidthCap.value} ${limitedBandwidthCap.suffix}`,
}}
</strong>
</Trans>
</>
) : null}
</div>
<Row gutter={6}>
<Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
<LoadingStatistic
isLoading={isTelemetryLoading}
title={t("common.blocks")}
value={telemetry[currentPercentile]?.blockCount}
/>
</Col>
<Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
<LoadingStatistic
isLoading={isTelemetryLoading}
title={t("pages.status.cemented")}
tooltip={t("tooltips.cemented")}
value={telemetry[currentPercentile]?.cementedCount}
/>
</Col>
<Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
<LoadingStatistic
isLoading={isTelemetryLoading}
title={t("pages.status.unchecked")}
tooltip={t("tooltips.unchecked")}
value={telemetry[currentPercentile]?.uncheckedCount}
/>
</Col>
<Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
<LoadingStatistic
isLoading={isTelemetryLoading}
title={t("common.accounts")}
value={telemetry[currentPercentile]?.accountCount}
/>
</Col>
<Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
<LoadingStatistic
isLoading={isTelemetryLoading}
title={t("pages.status.bandwidthCap")}
tooltip={t("tooltips.bandwidthCap")}
value={formattedMedianBandwidthCap.value || "∞"}
suffix={
formattedMedianBandwidthCap.value
? formattedMedianBandwidthCap.suffix
: null
}
/>
</Col>
<Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
<LoadingStatistic
isLoading={isTelemetryLoading}
title={t("pages.status.peers")}
value={telemetry[currentPercentile]?.peerCount}
/>
</Col>
<Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
<LoadingStatistic
isLoading={isTelemetryLoading}
title={t("pages.status.uptime")}
tooltip={t("tooltips.uptime")}
value={secondsToTime(telemetry[currentPercentile]?.uptime || 0)}
/>
</Col>
</Row>
</Card>
</Col>
<Col xs={24} md={8}>
<PieChart versions={versions} />
</Col>
</Row>
);
}
Example #21
Source File: index.tsx From fe-v5 with Apache License 2.0 | 4 votes |
export default function Dashboard() {
const { t } = useTranslation();
const [form] = Form.useForm();
const ref = useRef(null);
const history = useHistory();
const [isModalVisible, setIsModalVisible] = useState(false);
const [modalType, setModalType] = useState<ModalStatus>(ModalStatus.None);
const [selectRowKeys, setSelectRowKeys] = useState<number[]>([]);
const [exportData, setExportData] = useState<string>('');
const [editing, setEditing] = useState(false);
const [query, setQuery] = useState<string>('');
const [searchVal, setsearchVal] = useState<string>('');
const [dashboardList, setDashboardList] = useState<DashboardType[]>();
const [busiId, setBusiId] = useState<number>();
const showModal = () => {
setIsModalVisible(true);
};
const handleOk = async () => {
await form.validateFields();
if (editing) {
await edit();
message.success(t('编辑大盘成功'));
} else {
await create();
message.success(t('新建大盘成功'));
}
(ref?.current as any)?.refreshList();
setIsModalVisible(false);
setEditing(false);
};
useEffect(() => {
if (busiId) {
getDashboard(busiId).then((res) => {
if (searchVal && res.dat) {
const filters = searchVal.split(' ');
for (var i = 0; i < filters.length; i++) {
res.dat = res.dat.filter((item) => item.name.includes(filters[i]) || item.tags.includes(filters[i]));
}
}
setDashboardList(res.dat);
});
}
}, [busiId, searchVal]);
const create = async () => {
let { name, tags } = form.getFieldsValue();
return (
busiId &&
createDashboard(busiId, {
name,
tags,
})
);
};
const edit = async () => {
let { name, tags, id } = form.getFieldsValue();
return (
busiId &&
updateSingleDashboard(busiId, id, {
name,
tags,
pure: true,
})
);
};
const handleEdit = (record: DashboardType) => {
const { id, name, tags } = record;
form.setFieldsValue({
name,
tags,
id,
});
setIsModalVisible(true);
setEditing(true);
};
const handleTagClick = (tag) => {
const queryItem = query.length > 0 ? query.split(' ') : [];
if (queryItem.includes(tag)) return;
setQuery((query) => query + ' ' + tag);
setsearchVal((searchVal) => searchVal + ' ' + tag);
};
const layout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 16,
},
};
const dashboardColumn: ColumnsType = [
{
title: t('大盘名称'),
dataIndex: 'name',
render: (text: string, record: DashboardType) => {
const { t } = useTranslation();
return (
<div className='table-active-text' onClick={() => history.push(`/dashboard/${busiId}/${record.id}`)}>
{text}
</div>
);
},
},
{
title: t('分类标签'),
dataIndex: 'tags',
render: (text: string[]) => (
<>
{text.map((tag, index) => {
return tag ? (
<Tag
color='blue'
key={index}
style={{
cursor: 'pointer',
}}
onClick={() => handleTagClick(tag)}
>
{tag}
</Tag>
) : null;
})}
</>
),
},
{
title: t('更新时间'),
dataIndex: 'update_at',
render: (text: number) => dayjs(text * 1000).format('YYYY-MM-DD HH:mm:ss'),
},
{
title: t('发布人'),
dataIndex: 'create_by',
},
{
title: t('操作'),
width: '240px',
render: (text: string, record: DashboardType) => (
<div className='table-operator-area'>
<div className='table-operator-area-normal' onClick={() => handleEdit(record)}>
{t('编辑')}
</div>
<div
className='table-operator-area-normal'
onClick={async () => {
confirm({
title: `${t('是否克隆大盘')}${record.name}?`,
onOk: async () => {
await cloneDashboard(busiId as number, record.id);
message.success(t('克隆大盘成功'));
(ref?.current as any)?.refreshList();
},
onCancel() {},
});
}}
>
{t('克隆')}
</div>
<div
className='table-operator-area-warning'
onClick={async () => {
confirm({
title: `${t('是否删除大盘')}:${record.name}?`,
onOk: async () => {
await removeDashboard(busiId as number, record.id);
message.success(t('删除大盘成功'));
(ref?.current as any)?.refreshList();
},
onCancel() {},
});
}}
>
{t('删除')}
</div>
</div>
),
},
];
const onSearchQuery = (e) => {
let val = e.target.value;
setsearchVal(val);
};
const handleImportDashboard = async (data) => {
const { dat } = await importDashboard(busiId as number, data);
return dat || {};
};
return (
<PageLayout title={t('监控大盘')} icon={<FundViewOutlined />} hideCluster={true}>
<div style={{ display: 'flex' }}>
<LeftTree busiGroup={{ onChange: (id) => setBusiId(id) }}></LeftTree>
{busiId ? (
<div className='dashboard' style={{ flex: 1, overflow: 'auto' }}>
<div className='table-handle'>
<div className='table-handle-search'>
<Input
onPressEnter={onSearchQuery}
className={'searchInput'}
value={query}
onChange={(e) => setQuery(e.target.value)}
prefix={<SearchOutlined />}
placeholder={t('大盘名称、分类标签')}
/>
</div>
<div className='table-handle-buttons'>
<Button type='primary' onClick={showModal} ghost>
{t('新建大盘')}
</Button>
<div className={'table-more-options'}>
<Dropdown
overlay={
<ul className='ant-dropdown-menu'>
<li className='ant-dropdown-menu-item' onClick={() => setModalType(ModalStatus.BuiltIn)}>
<span>{t('导入监控大盘')}</span>
</li>
<li
className='ant-dropdown-menu-item'
onClick={async () => {
if (selectRowKeys.length) {
let exportData = await exportDashboard(busiId as number, selectRowKeys);
setExportData(JSON.stringify(exportData.dat, null, 2));
setModalType(ModalStatus.Export);
} else {
message.warning(t('未选择任何大盘'));
}
}}
>
<span>{t('导出监控大盘')}</span>
</li>
<li
className='ant-dropdown-menu-item'
onClick={() => {
if (selectRowKeys.length) {
confirm({
title: '是否批量删除大盘?',
onOk: async () => {
const reuqests = selectRowKeys.map((id) => {
console.log(id);
return removeDashboard(busiId as number, id);
});
Promise.all(reuqests).then(() => {
message.success(t('批量删除大盘成功'));
});
// TODO: 删除完后立马刷新数据有时候不是实时的,这里暂时间隔0.5s后再刷新列表
setTimeout(() => {
(ref?.current as any)?.refreshList();
}, 500);
},
onCancel() {},
});
} else {
message.warning(t('未选择任何大盘'));
}
}}
>
<span>{t('批量删除大盘')}</span>
</li>
</ul>
}
trigger={['click']}
>
<Button onClick={(e) => e.stopPropagation()}>
{t('更多操作')}
<DownOutlined
style={{
marginLeft: 2,
}}
/>
</Button>
</Dropdown>
</div>
</div>
</div>
<Table
dataSource={dashboardList}
className='dashboard-table'
columns={dashboardColumn}
pagination={{
total: dashboardList?.length,
showTotal(total: number) {
return `共 ${total} 条数据`;
},
pageSizeOptions: [30, 50, 100, 300],
defaultPageSize: 30,
showSizeChanger: true,
}}
rowKey='id'
rowSelection={{
selectedRowKeys: selectRowKeys,
onChange: (selectedRowKeys: number[]) => {
setSelectRowKeys(selectedRowKeys);
},
}}
></Table>
</div>
) : (
<BlankBusinessPlaceholder text='监控大盘' />
)}
</div>
<Modal
title={editing ? t('编辑监控大盘') : t('创建新监控大盘')}
visible={isModalVisible}
onOk={handleOk}
onCancel={() => {
setIsModalVisible(false);
}}
destroyOnClose
>
<Form {...layout} form={form} preserve={false}>
<Form.Item
label={t('大盘名称')}
name='name'
wrapperCol={{
span: 24,
}}
rules={[
{
required: true,
message: t('请输入大盘名称'),
},
]}
>
<Input />
</Form.Item>
<Form.Item
wrapperCol={{
span: 24,
}}
label={t('分类标签')}
name='tags'
>
<Select
mode='tags'
dropdownStyle={{
display: 'none',
}}
placeholder={t('请输入分类标签(请用回车分割)')}
></Select>
</Form.Item>
<Form.Item name='id' hidden>
<Input />
</Form.Item>
</Form>
</Modal>
<ImportAndDownloadModal
bgid={busiId}
status={modalType}
crossCluster={false}
fetchBuiltinFunc={getBuiltinDashboards}
submitBuiltinFunc={createBuiltinDashboards}
onClose={() => {
setModalType(ModalStatus.None);
}}
onSuccess={() => {
(ref?.current as any)?.refreshList();
}}
onSubmit={handleImportDashboard}
label='大盘'
title={
ModalStatus.Export === modalType ? (
'大盘'
) : (
<Tabs defaultActiveKey={ModalStatus.BuiltIn} onChange={(e: ModalStatus) => setModalType(e)} className='custom-import-alert-title'>
<TabPane tab=' 导入内置大盘模块' key={ModalStatus.BuiltIn}></TabPane>
<TabPane tab='导入大盘JSON' key={ModalStatus.Import}></TabPane>
</Tabs>
)
}
exportData={exportData}
/>
</PageLayout>
);
}
Example #22
Source File: Members.tsx From posthog-foss with MIT License | 4 votes |
function LevelComponent(member: OrganizationMemberType): JSX.Element | null {
const { user } = useValues(userLogic)
const { currentOrganization } = useValues(organizationLogic)
const { changeMemberAccessLevel } = useActions(membersLogic)
const myMembershipLevel = currentOrganization ? currentOrganization.membership_level : null
if (!user) {
return null
}
function generateHandleClick(listLevel: OrganizationMembershipLevel): (event: React.MouseEvent) => void {
return function handleClick(event: React.MouseEvent) {
event.preventDefault()
if (!user) {
throw Error
}
if (listLevel === OrganizationMembershipLevel.Owner) {
Modal.confirm({
centered: true,
title: `Transfer organization ownership to ${member.user.first_name}?`,
content: `You will no longer be the owner of ${user.organization?.name}. After the transfer you will become an administrator.`,
icon: <SwapOutlined />,
okType: 'danger',
okText: 'Transfer Ownership',
onOk() {
changeMemberAccessLevel(member, listLevel)
},
})
} else {
changeMemberAccessLevel(member, listLevel)
}
}
}
const levelButton = (
<Button
data-attr="change-membership-level"
icon={member.level === OrganizationMembershipLevel.Owner ? <CrownFilled /> : undefined}
>
{membershipLevelToName.get(member.level) ?? `unknown (${member.level})`}
</Button>
)
const allowedLevels = organizationMembershipLevelIntegers.filter(
(listLevel) => !getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, listLevel)
)
const disallowedReason = getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, allowedLevels)
return disallowedReason ? (
<Tooltip title={disallowedReason}>{levelButton}</Tooltip>
) : (
<Dropdown
overlay={
<Menu>
{allowedLevels.map((listLevel) => (
<Menu.Item key={`${member.user.uuid}-level-${listLevel}`}>
<a href="#" onClick={generateHandleClick(listLevel)} data-test-level={listLevel}>
{listLevel === OrganizationMembershipLevel.Owner ? (
<>
<CrownFilled style={{ marginRight: '0.5rem' }} />
Transfer organization ownership
</>
) : listLevel > member.level ? (
<>
<UpOutlined style={{ marginRight: '0.5rem' }} />
Upgrade to {membershipLevelToName.get(listLevel)}
</>
) : (
<>
<DownOutlined style={{ marginRight: '0.5rem' }} />
Downgrade to {membershipLevelToName.get(listLevel)}
</>
)}
</a>
</Menu.Item>
))}
</Menu>
}
>
{levelButton}
</Dropdown>
)
}
Example #23
Source File: index.tsx From surveyo with Apache License 2.0 | 4 votes |
function FormCreator() {
const [questions, setQuestions] = useState<any>([]);
const [formSubmitted, setFormSubmitState] = useState(false);
const [formURL, setFormURL] = useState('');
const [surveyTitle, setSurveyTitle] = useState('');
const [formHook] = useForm();
const {user} = useAuth0();
const [sendToClient] = useMutation(ADD_FORM);
const getCard = (i: number) => {
const question = questions[i];
const params = {
question: question,
updateQuestion: (question: any) =>
setQuestions(update(questions, {$splice: [[i, 1, question]]})),
deleteQuestion: () =>
setQuestions(update(questions, {$splice: [[i, 1]]})),
};
return <QuestionCard {...params} />;
};
const menu = (
<Menu onClick={e => setQuestions(questions.concat({type: e.key}))}>
<Menu.Item key="Text">Short Answer</Menu.Item>
<Menu.Item key="SingleChoice">Multiple Choice</Menu.Item>
<Menu.Item key="Date">Date</Menu.Item>
<Menu.Item key="Rating">Rating</Menu.Item>
<Menu.Item key="NetPromoterScore">Net Promoter Score</Menu.Item>
</Menu>
);
if (formSubmitted) {
return (
<Card type="inner">
<Result
status="success"
title="Thank you!"
subTitle={
<Anchor>
<Anchor.Link href={formURL} title="Your form is live." />
</Anchor>
}
/>
</Card>
);
} else
return (
<PageHeader ghost={true} title="Create a survey">
<Form form={formHook}>
<Card
actions={[
<Dropdown overlay={menu}>
<Button>
Add Question <DownOutlined />
</Button>
</Dropdown>,
<Button
type="primary"
onClick={async () => {
const values = await formHook.validateFields();
console.log('validation ' + values.name);
for (let index = 0; index < questions.length; index++) {
if ('options' in questions[index]) {
let newOptions = questions[index].options.map(
(value: any, index: any) => {
return {order: index, title: value};
}
);
questions[index].options = newOptions;
}
questions[index].order = index;
if (!('required' in questions[index])) {
questions[index].required = false;
}
}
var form = {
title: surveyTitle,
fields: questions,
creator: {email: user.email},
};
console.log('Form: ', form);
try {
var result = await sendToClient({
variables: {
form: form,
},
});
console.log(result);
let id = result.data.addForm.form[0].id;
let url =
window.location.href.replace('/create', '') +
'/form/' +
id;
setFormURL(url);
setFormSubmitState(true);
} catch (error) {
console.log(error);
}
}}
>
Create
</Button>,
]}
>
<Form.Item
label="Survey Title"
name="survey title"
rules={[{required: true, message: 'Please input Survey title'}]}
>
<Input
placeholder="Enter your survey title"
onChange={e => {
setSurveyTitle(e.target.value);
}}
/>
</Form.Item>
{questions.map((question: any, index: number) => (
<div key={index}>
<Card>{getCard(index)}</Card>
</div>
))}
</Card>
</Form>
</PageHeader>
);
}
Example #24
Source File: index.tsx From nanolooker with MIT License | 4 votes |
LargeTransactions: React.FC = () => {
const { t } = useTranslation();
const history = useHistory();
const { sortBy: paramSortBy = SORT_BY.LATEST } = useParams<PageParams>();
const { largeTransactions, isLoading } = useLargeTransactions();
const [currentPage, setCurrentPage] = React.useState<number>(1);
const [orderedTransactions, setOrderedTransactions] = React.useState<
Transaction[]
>();
const defaultSortBy = Object.values(SORT_BY).includes(paramSortBy)
? paramSortBy
: SORT_BY.LATEST;
const [sortBy, setSortBy] = React.useState(defaultSortBy);
const showPaginate = largeTransactions.length > TRANSACTIONS_PER_PAGE;
React.useEffect(() => {
const start = 0 + (currentPage - 1) * TRANSACTIONS_PER_PAGE;
const data = largeTransactions.map(
({ timestamp, block, hash, amount }) => ({
...block,
amount,
hash,
largest: new BigNumber(rawToRai(amount)).toNumber(),
latest: timestamp,
local_timestamp: Math.floor(timestamp / 1000),
}),
);
const orderedData = orderBy(data, [sortBy.toLowerCase()], ["desc"]);
const pageData = orderedData?.slice(start, start + TRANSACTIONS_PER_PAGE);
// @ts-ignore
setOrderedTransactions(pageData);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [largeTransactions, sortBy, currentPage]);
const handleSortBy = ({ key }: any) => {
history.replace(`/large-transactions/${key}`);
setCurrentPage(1);
setSortBy(key);
};
return (
<>
<Helmet>
<title>Nano {t("menu.largeTransactions")}</title>
</Helmet>
<Title level={3}>{t("menu.largeTransactions")}</Title>
<div style={{ marginBottom: "12px" }}>
<Text>{t("pages.largeTransactions.description")}</Text>
<Tooltip placement="right" title={t("tooltips.largeTransactions")}>
<QuestionCircle />
</Tooltip>
</div>
<div style={{ marginBottom: "12px" }}>
<Dropdown
overlay={
<Menu onClick={handleSortBy}>
{Object.values(SORT_BY).map(sortBy => (
<Menu.Item key={sortBy}>
{t(`pages.largeTransactions.${sortBy}`)}
</Menu.Item>
))}
</Menu>
}
>
<Button>
{t(`pages.largeTransactions.${sortBy}`)} <DownOutlined />
</Button>
</Dropdown>
</div>
<TransactionsTable
data={orderedTransactions}
isLoading={isLoading}
isPaginated={true}
showPaginate={showPaginate}
pageSize={TRANSACTIONS_PER_PAGE}
currentPage={currentPage}
totalPages={largeTransactions.length}
setCurrentPage={setCurrentPage}
/>
</>
);
}
Example #25
Source File: LabelsValues.tsx From fe-v5 with Apache License 2.0 | 4 votes |
export default function LabelsValues(props: IProps) {
const { value, range, onChange } = props;
const { id, refreshFlag, filters, dynamicLabels, dimensionLabels } = value;
const [labelValues, setLabelValues] = useState<{ [key: string]: string[] }>({});
const [dimensionLabelsValues, setDimensionLabelsValues] = useState<{ [key: string]: string[] }>({});
const [dimensionLabelsSearch, setDimensionLabelsSearch] = useState({});
const filtersStr = getFiltersStr(filters);
const dynamicLabelsStr = getDynamicLabelsStr(dynamicLabels);
const [expaned, setExpaned] = useState({
filters: false,
dynamicLabels: true,
});
useEffect(() => {
const dynamicLabelsRequests = _.map(dynamicLabels, (item) => {
return getLabelValues(item.label, range, filtersStr ? `{${filtersStr}}` : '');
});
Promise.all(dynamicLabelsRequests).then((res) => {
const _labelValues = {};
_.forEach(res, (item, idx) => {
_labelValues[dynamicLabels[idx].label] = item;
});
setLabelValues(_labelValues);
});
}, [filtersStr]);
useEffect(() => {
const matchStr = _.join(_.compact(_.concat(filtersStr, dynamicLabelsStr)), ',');
const dimensionLabelsRequests = _.map(dimensionLabels, (item) => {
return getLabelValues(item.label, range, matchStr ? `{${matchStr}}` : '');
});
Promise.all(dimensionLabelsRequests).then((res) => {
const _labelValues = {};
_.forEach(res, (item, idx) => {
_labelValues[dimensionLabels[idx].label] = item;
});
setDimensionLabelsValues(_labelValues);
if (_.every(dimensionLabels, (item) => _.isEmpty(item.value))) {
onChange({
...value,
dimensionLabels: _.map(dimensionLabels, (item, idx) => {
if (idx === 0) {
return {
label: item.label,
value: _.slice(_labelValues[item.label], 0, 10),
};
}
return item;
}),
});
}
});
}, [filtersStr, dynamicLabelsStr, id, refreshFlag]);
return (
<div className='n9e-metric-views-labels-values'>
{!_.isEmpty(filtersStr) && (
<div className='n9e-metric-views-labels-values-item'>
<div
className='page-title'
style={{ cursor: 'pointer' }}
onClick={() => {
setExpaned({
...expaned,
filters: !expaned.filters,
});
}}
>
前置过滤条件 {expaned.filters ? <UpOutlined /> : <DownOutlined />}
</div>
{expaned.filters && <div className='n9e-metric-views-filters'>{filtersStr ? filtersStr : '暂无数据'}</div>}
</div>
)}
{!_.isEmpty(dynamicLabels) && (
<div className='n9e-metric-views-labels-values-item'>
<div
className='page-title'
style={{ cursor: 'pointer' }}
onClick={() => {
setExpaned({
...expaned,
dynamicLabels: !expaned.dynamicLabels,
});
}}
>
动态过滤条件 {expaned.dynamicLabels ? <UpOutlined /> : <DownOutlined />}
</div>
{expaned.dynamicLabels && (
<div className='n9e-metric-views-dynamicLabels'>
{_.isEmpty(dynamicLabels) ? (
<div style={{ marginBottom: 10 }}>暂无数据</div>
) : (
_.map(dynamicLabels, (item) => {
return (
<div key={item.label} className='n9e-metric-views-dynamicLabels-item'>
<div className='n9e-metric-views-dynamicLabels-item-label'>{item.label}:</div>
<Select
allowClear
showSearch
style={{ width: '100%' }}
value={item.value}
onChange={(val) => {
const _dynamicLabels = _.map(dynamicLabels, (obj) => {
if (item.label === obj.label) {
return {
...obj,
value: val,
};
}
return obj;
});
onChange({
...value,
dynamicLabels: _dynamicLabels,
dimensionLabels: _.map(dimensionLabels, (item, idx) => {
if (idx === 0) {
return {
label: item.label,
value: [],
};
}
return item;
}),
});
}}
>
{_.map(labelValues[item.label], (value) => {
return (
<Select.Option key={value} value={value}>
{value}
</Select.Option>
);
})}
</Select>
</div>
);
})
)}
</div>
)}
</div>
)}
{_.map(dimensionLabels, (dimensionLabel) => {
const dimensionLabelValues = dimensionLabelsValues[dimensionLabel.label];
return (
<div key={dimensionLabel.label} className='n9e-metric-views-labels-values-item'>
<div className='page-title'>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-between',
alignItems: 'center',
width: 220,
}}
>
<Tooltip title={dimensionLabel.label} placement='left'>
<div
style={{
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: 'calc(100% - 30px)',
}}
>
{dimensionLabel.label}
</div>
</Tooltip>
<a
style={{ fontSize: 12, fontWeight: 'normal' }}
onClick={() => {
onChange({
...value,
dimensionLabels: _.map(dimensionLabels, (item) => {
if (item.label === dimensionLabel.label) {
return {
...item,
value: dimensionLabelValues,
};
}
return item;
}),
});
}}
>
全选
</a>
</div>
</div>
<div className='n9e-metric-views-dimensionLabel'>
<Input.Group compact>
<Input
style={{ width: 'calc(100% - 32px)' }}
prefix={<SearchOutlined />}
value={dimensionLabelsSearch[dimensionLabel.label]}
onChange={(e) => {
setDimensionLabelsSearch({
...dimensionLabelsSearch,
[dimensionLabel.label]: e.target.value,
});
}}
/>
<Tooltip title='清空已选的值' placement='right' getTooltipContainer={() => document.body}>
<Button
icon={<ClearOutlined />}
onClick={() => {
onChange({
...value,
dimensionLabels: _.map(dimensionLabels, (item) => {
if (item.label === dimensionLabel.label) {
return {
...item,
value: [],
};
}
return item;
}),
});
}}
/>
</Tooltip>
</Input.Group>
<div className='n9e-metric-views-dimensionLabel-content'>
{_.isEmpty(dimensionLabelValues) ? (
'暂无数据'
) : (
<div>
{_.map(
_.filter(dimensionLabelValues, (item) => {
let result = true;
if (dimensionLabelsSearch[dimensionLabel.label]) {
try {
const reg = new RegExp(dimensionLabelsSearch[dimensionLabel.label], 'gi');
result = reg.test(item);
} catch (e) {
console.log(e);
}
}
return result;
}),
(item: string) => {
return (
<div
key={item}
className={classNames({
'n9e-metric-views-dimensionLabel-content-item': true,
active: _.includes(dimensionLabel.value, item),
})}
onClick={() => {
const dimensionLabelValue = _.includes(dimensionLabel.value, item) ? _.without(dimensionLabel.value, item) : _.concat(dimensionLabel.value, item);
const newDimensionLabels = _.map(dimensionLabels, (item) => {
if (item.label === dimensionLabel.label) {
return {
...item,
value: _.compact(dimensionLabelValue),
};
}
return {
...item,
value: [],
};
});
onChange({
...value,
dimensionLabels: newDimensionLabels,
});
}}
>
{item}
</div>
);
},
)}
</div>
)}
</div>
</div>
</div>
);
})}
</div>
);
}
Example #26
Source File: index.tsx From nanolooker with MIT License | 4 votes |
NewsPage: React.FC = () => {
const { t } = useTranslation();
const history = useHistory();
const { feed = "" } = useParams<PageParams>();
const [posts, setPosts] = React.useState([] as (MediumPost | YoutubePost)[]);
const [authors, setAuthors] = React.useState([""]);
const {
isLoading: isMediumLoading,
posts: mediumPosts,
authors: mediumAuthors,
} = useMedium();
const {
isLoading: isYoutubeLoading,
posts: youtubePosts,
authors: youtubeAuthors,
} = useYoutube();
const [feedFilter, setFeedFilter] = React.useState<MEDIUM_FEEDS | string>(
ALL,
);
const [authorFilter, setAuthorFilter] = React.useState<string>(ALL);
const [sourceFilter, setSourceFilter] = React.useState<string>(ALL);
React.useEffect(() => {
const orderedPosts: (MediumPost | YoutubePost)[] = orderBy(
[...mediumPosts, ...youtubePosts],
["pubDate"],
["desc"],
);
setPosts(orderedPosts);
}, [mediumPosts, youtubePosts]);
React.useEffect(() => {
const orderedAuthors: string[] = [
...mediumAuthors,
...youtubeAuthors,
].sort();
setAuthors(orderedAuthors);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mediumAuthors.length, youtubeAuthors.length]);
React.useEffect(() => {
if (feed && posts.find(({ feed: postFeed }) => postFeed === feed)) {
handleFeedFilter({ key: feed });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [posts]);
const handleFeedFilter = ({ key }: any) => {
history.replace(`/news${key !== ALL ? `/${key}` : ""}`);
setFeedFilter(key);
};
const handleAuthorFilter = ({ key }: any) => {
setAuthorFilter(key);
};
const handleSourceFilter = ({ key }: any) => {
setSourceFilter(key);
};
const filteredPosts = posts
.filter(({ feed }) => (feedFilter !== ALL ? feedFilter === feed : true))
.filter(({ author }) =>
authorFilter !== ALL ? authorFilter === author : true,
)
.filter(({ source }) =>
sourceFilter !== ALL ? sourceFilter === source : true,
);
return (
<>
<Helmet>
<title>Nano {t("menu.news")}</title>
</Helmet>
<Space style={{ marginBottom: "12px" }}>
<Dropdown
overlay={
<Menu onClick={handleFeedFilter}>
<Menu.Item key={ALL}>{t("pages.news.allFeeds")}</Menu.Item>
{Object.values(MEDIUM_FEEDS).map(feed => (
<Menu.Item key={feed}>{feed}</Menu.Item>
))}
</Menu>
}
>
<Button>
{feedFilter !== ALL ? feedFilter : t("pages.news.allFeeds")}{" "}
<DownOutlined />
</Button>
</Dropdown>
<Dropdown
overlay={
<Menu onClick={handleAuthorFilter}>
<Menu.Item key={ALL}>{t("pages.news.allAuthors")}</Menu.Item>
{authors.map(author => (
<Menu.Item key={author}>{author}</Menu.Item>
))}
</Menu>
}
>
<Button>
{authorFilter !== ALL ? authorFilter : t("pages.news.allAuthors")}{" "}
<DownOutlined />
</Button>
</Dropdown>
<Dropdown
overlay={
<Menu onClick={handleSourceFilter}>
<Menu.Item key={ALL}>{t("pages.news.allSources")}</Menu.Item>
{Object.keys(PostSource).map(source => (
<Menu.Item key={source}>{source}</Menu.Item>
))}
</Menu>
}
>
<Button>
{sourceFilter !== ALL ? sourceFilter : t("pages.news.allSources")}{" "}
<DownOutlined />
</Button>
</Dropdown>
</Space>
<Row gutter={[12, 0]}>
{!filteredPosts.length && (isMediumLoading || isYoutubeLoading)
? Array.from({ length: 6 }).map((_value, index) => (
<Col xs={24} md={12} lg={8} key={index}>
<Card
size="small"
bodyStyle={{
minHeight: "240px",
}}
>
<Skeleton active loading={true}></Skeleton>
</Card>
</Col>
))
: null}
{filteredPosts.map((post: MediumPost | YoutubePost, index) => (
<Col xs={24} md={12} lg={8} key={index}>
{post.source === PostSource.MEDIUM ? <Medium post={post} /> : null}
{post.source === PostSource.YOUTUBE ? (
<Youtube post={post} />
) : null}
</Col>
))}
</Row>
{!filteredPosts?.length && !isMediumLoading && !isYoutubeLoading ? (
<Text style={{ display: "block" }}>{t("common.noResults")}</Text>
) : null}
</>
);
}
Example #27
Source File: ChartDraggableElementField.tsx From datart with Apache License 2.0 | 4 votes |
ChartDraggableElementField: FC<{
modalSize;
config;
columnConfig;
ancestors;
aggregation;
availableSourceFunctions;
onConfigChanged;
handleOpenActionModal;
}> = memo(
({
modalSize,
config,
columnConfig,
ancestors,
aggregation,
availableSourceFunctions,
onConfigChanged,
handleOpenActionModal,
}) => {
const renderActionExtensionMenu = (uid: string, type: string, category) => {
return (
<ChartDataConfigSectionActionMenu
uid={uid}
type={type}
category={category}
ancestors={ancestors}
config={config}
modalSize={modalSize}
availableSourceFunctions={availableSourceFunctions}
onConfigChanged={onConfigChanged}
onOpenModal={handleOpenActionModal}
/>
);
};
const enableActionsIcons = col => {
const icons = [] as any;
if (col.alias) {
icons.push(<DiffOutlined key="alias" />);
}
if (col.sort) {
if (col.sort.type === SortActionType.ASC) {
icons.push(<SortAscendingOutlined key="sort" />);
}
if (col.sort.type === SortActionType.DESC) {
icons.push(<SortDescendingOutlined key="sort" />);
}
}
if (col.format) {
icons.push(<FormatPainterOutlined key="format" />);
}
if (col.aggregate) {
icons.push(<GroupOutlined key="aggregate" />);
}
if (col.filter) {
icons.push(<FilterOutlined key="filter" />);
}
if (col.color) {
icons.push(<BgColorsOutlined key="color" />);
}
if (col.size) {
icons.push(<FontSizeOutlined key="size" />);
}
return icons;
};
return (
<Dropdown
key={columnConfig.uid}
disabled={!config?.actions}
destroyPopupOnHide={true}
overlay={renderActionExtensionMenu(
columnConfig.uid!,
columnConfig.type,
columnConfig.category,
)}
overlayClassName="datart-data-section-dropdown"
trigger={['click']}
>
<div>
{config?.actions && <DownOutlined style={{ marginRight: '10px' }} />}
<span>
{aggregation === false
? columnConfig.colName
: getColumnRenderName(columnConfig)}
</span>
<div style={{ display: 'inline-block', marginLeft: '5px' }}>
{enableActionsIcons(columnConfig)}
</div>
</div>
</Dropdown>
);
},
)
Example #28
Source File: NavBar.tsx From office-hours with GNU General Public License v3.0 | 4 votes |
export default function NavBar({ courseId }: NavBarProps): ReactElement {
const profile = useProfile();
if (!courseId) {
courseId = profile?.courses[0]?.course?.id;
}
const [_defaultCourse, setDefaultCourse] = useLocalStorage(
"defaultCourse",
null
);
const [visible, setVisible] = useState<boolean>(false);
const { pathname } = useRouter();
const { course } = useCourse(courseId);
const role = useRoleInCourse(courseId);
const openQueues = course?.queues?.filter((queue) => queue.isOpen);
const showDrawer = () => {
setVisible(true);
};
const onClose = () => {
setVisible(false);
};
const courseSelector = (
<Menu>
{profile?.courses?.map((c) => (
<CoursesMenuItem
key={c.course.id}
onClick={() => setDefaultCourse(c.course)}
>
<Link href="/course/[cid]/today" as={`/course/${c.course.id}/today`}>
<a>{c.course.name}</a>
</Link>
</CoursesMenuItem>
))}
</Menu>
);
const tabs: NavBarTabsItem[] = [
{
href: "/course/[cid]/today",
as: `/course/${courseId}/today`,
text: "Today",
},
{
href: "/course/[cid]/schedule",
as: `/course/${courseId}/schedule`,
text: "Schedule",
},
];
if (role === Role.PROFESSOR) {
tabs.push({
href: "/course/[cid]/course_admin_panel",
as: `/course/${courseId}/course_admin_panel`,
text: "Admin Panel",
});
}
if (openQueues?.length > 0) {
tabs.push({
text: "Queue",
queues: openQueues,
courseId: courseId,
});
}
if (role === Role.PROFESSOR) {
tabs.push({
href: "/course/[cid]/insights",
as: `/course/${courseId}/insights`,
text: "Insights",
});
}
return courseId ? (
<>
<NavBG />
<AlertsContainer courseId={courseId} />
<Nav>
<LogoContainer>
{profile?.courses.length > 1 ? (
<Dropdown
overlay={courseSelector}
trigger={["click"]}
placement="bottomLeft"
>
<a>
<Logo>
<span>{course?.name}</span>
<DownOutlined
style={{
fontSize: "16px",
verticalAlign: "-0.125em",
marginLeft: "5px",
}}
/>
</Logo>
</a>
</Dropdown>
) : (
<Logo>
<span>{course?.name}</span>
</Logo>
)}
</LogoContainer>
<MenuCon>
<LeftMenu>
<NavBarTabs horizontal currentHref={pathname} tabs={tabs} />
</LeftMenu>
<RightMenu>
<ProfileDrawer courseId={courseId} />
</RightMenu>
</MenuCon>
<BarsMenu type="primary" onClick={showDrawer}>
<BarsButton />
</BarsMenu>
<Drawer
title="Course"
placement="right"
visible={visible}
closable={false}
onClose={onClose}
bodyStyle={{ padding: "12px" }}
>
<NavBarTabs currentHref={pathname} tabs={tabs} />
<ProfileDrawer courseId={courseId} />
</Drawer>
</Nav>
</>
) : null;
}
Example #29
Source File: HeadAccounts.tsx From subscan-multisig-react with Apache License 2.0 | 4 votes |
HeadAccounts = () => {
const { t } = useTranslation();
const { network, accounts, extensions } = useApi();
const { contacts, queryContacts } = useContacts();
const [popoverVisible, setPopoverVisible] = useState(false);
const [drawerVisible, setDrawerVisible] = useState(false);
const [addContactModalVisible, setAddContactModalVisible] = useState(false);
const headerLinkStyle = useMemo(() => genHeaderLinkStyle`${network}`, [network]);
const mainColor = useMemo(() => {
return getThemeColor(network);
}, [network]);
useEffect(() => {
if (popoverVisible) {
queryContacts();
}
}, [popoverVisible, queryContacts]);
const renderAccountContent = () => {
if (extensions && extensions.length === 0) {
return <div className="mx-5 my-3">{t('extension not found')}</div>;
}
if (accounts && accounts.length === 0) {
return <div className="mx-5 my-3">{t('extension account empty')}</div>;
}
return accounts?.map((item) => (
<AccountItem key={item.address} address={item.address} name={item.meta?.name} type="injected" />
));
};
return (
<div>
<span className={`${headerLinkStyle} inline md:hidden`} onClick={() => setDrawerVisible(true)}>
{t('accounts')}
<DownOutlined style={{ marginTop: '4px' }} />
</span>
<Drawer
className="block md:hidden account-drawer"
placement="top"
visible={drawerVisible}
onClose={() => setDrawerVisible(false)}
>
<div>
<Tabs defaultActiveKey="1">
<TabPane tab={t('My Account')} key="1">
<div className="account-list">{renderAccountContent()}</div>
</TabPane>
<TabPane tab={t('Contact Account')} key="2">
<div>
<div className="account-list">
{contacts?.map((item) => (
<AccountItem
key={item.address}
address={item.address}
name={item.meta?.name}
type="contact"
refreshContacts={queryContacts}
/>
))}
</div>
<div className="flex justify-end md:mt-2">
<Button
type="default"
size="large"
className="flex justify-center items-center w-full"
style={{ color: mainColor }}
onClick={() => {
setAddContactModalVisible(true);
setPopoverVisible(false);
}}
>
{t('contact.Add Contact')}
</Button>
</div>
</div>
</TabPane>
</Tabs>
</div>
</Drawer>
<Popover
title={null}
trigger="click"
visible={popoverVisible}
onVisibleChange={setPopoverVisible}
overlayInnerStyle={{
borderRadius: '0.15rem',
}}
overlayStyle={{
width: '600px',
}}
content={
<div>
<Tabs defaultActiveKey="1">
<TabPane tab={t('My Account')} key="1">
<div className="account-list">{renderAccountContent()}</div>
</TabPane>
<TabPane tab={t('Contact Account')} key="2">
<div className="">
<div className="account-list">
{contacts?.map((item) => (
<AccountItem
key={item.address}
address={item.address}
name={item.meta?.name}
type="contact"
refreshContacts={queryContacts}
/>
))}
</div>
<div className="flex justify-end md:mt-2">
<Button
type="default"
size="large"
className="flex justify-center items-center w-full"
style={{ color: mainColor }}
onClick={() => {
setAddContactModalVisible(true);
setPopoverVisible(false);
}}
>
{t('contact.Add Contact')}
</Button>
</div>
</div>
</TabPane>
</Tabs>
</div>
}
placement="bottom"
>
<span className={`${headerLinkStyle} hidden md:block`}>
{t('accounts')}
<DownOutlined style={{ marginTop: '4px' }} />
</span>
</Popover>
<AddContactModal visible={addContactModalVisible} handleVisibleChange={setAddContactModalVisible} />
</div>
);
}