@ant-design/icons#DownloadOutlined TypeScript Examples
The following examples show how to use
@ant-design/icons#DownloadOutlined.
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 surveyo with Apache License 2.0 | 6 votes |
function DownloadCsv(props: any) {
const [getCsv, {loading, error, data}] = useLazyQuery<
GetCsvResponses,
GetCsvResponsesVariables
>(GET_CSV);
if (error) {
console.error(error);
message.error('Internal error: could not generate CSV');
}
return data ? (
<Tooltip title="Download CSV">
<CSVLink data={makeCsv(data)} filename={`${props.title}.csv`}>
<Button type="link" icon={<DownloadOutlined />} />
</CSVLink>
</Tooltip>
) : (
<Tooltip title="Generate CSV">
<Button
type="link"
icon={loading ? <LoadingOutlined /> : <FileExcelOutlined />}
onClick={() => getCsv({variables: {id: props.id}})}
/>
</Tooltip>
);
}
Example #2
Source File: out.tsx From web-pdm with Apache License 2.0 | 6 votes |
IconRenders = {
undo: <RollbackOutlined />,
redo: <RollbackOutlined style={{ transform: 'scaleX(-1)' }} />,
min: <ZoomOutOutlined />,
max: <ZoomInOutlined />,
full: <BorderOutlined />,
miniMap: <PictureFilled />,
miniMapNo: <PictureOutlined />,
dagreLayout: <PartitionOutlined />,
relationLayout: <UngroupOutlined />,
reload: <ReloadOutlined />,
image: <DownloadOutlined />,
darkness: <SnippetsFilled />,
light: <SnippetsOutlined />,
colorClose: <BgColorsOutlined />,
colorOpen: <BgColorsOutlined />
}
Example #3
Source File: seeds.tsx From dendron with GNU Affero General Public License v3.0 | 6 votes |
/**
* Component for Button to Add Seed to Workspace on a seed browser card
* @param existsInWorkspace - does the seed already exist in the users workspace?
* @param seedId - seed unique ID
* @returns
*/
export function AddToWorkspaceButton({
existsInWorkspace,
seedId,
}: {
existsInWorkspace: boolean;
seedId: string;
}) {
function onClick() {
postVSCodeMessage({
type: SeedBrowserMessageType.onSeedAdd,
data: { data: seedId },
source: DMessageSource.webClient,
} as SeedBrowserMessage);
}
if (!existsInWorkspace) {
return (
<Tooltip placement="top" title="Add to Workspace">
<DownloadOutlined key="download" onClick={onClick} />
</Tooltip>
);
}
return (
<Tooltip placement="top" title="Already in Workspace">
<CheckCircleOutlined key="installed" disabled />
</Tooltip>
);
}
Example #4
Source File: Seeds.tsx From dendron with GNU Affero General Public License v3.0 | 6 votes |
/**
* Component for Button to Add Seed to Workspace on a seed browser card
* @param existsInWorkspace - does the seed already exist in the users workspace?
* @param seedId - seed unique ID
* @returns
*/
export function AddToWorkspaceButton({
existsInWorkspace,
seedId,
}: {
existsInWorkspace: boolean;
seedId: string;
}) {
function onClick() {
postVSCodeMessage({
type: SeedBrowserMessageType.onSeedAdd,
data: { data: seedId },
source: DMessageSource.webClient,
} as SeedBrowserMessage);
}
if (!existsInWorkspace) {
return (
<Tooltip placement="top" title="Add to Workspace">
<DownloadOutlined key="download" onClick={onClick} />
</Tooltip>
);
}
return (
<Tooltip placement="top" title="Already in Workspace">
<CheckCircleOutlined key="installed" disabled />
</Tooltip>
);
}
Example #5
Source File: index.tsx From LogicFlow with Apache License 2.0 | 6 votes |
export default function SnapshotExample() {
useEffect(() => {
LogicFlow.use(Snapshot);
lf = new LogicFlow({
...config,
container: document.querySelector('#graph') as HTMLElement
});
lf.render(data)
}, []);
const handleDoenload = () => {
lf.getSnapshot();
}
return (
<>
<ExampleHeader
contentStyle={contentStyle}
githubPath="/extension/snapshot/index.tsx"
>
导出图片:
<Button
shape="round"
icon={<DownloadOutlined />}
onClick={handleDoenload}
/>
</ExampleHeader>
<div id="graph" className="viewport" />
</>
)
}
Example #6
Source File: index.tsx From web-pdm with Apache License 2.0 | 6 votes |
IconRenders = {
undo: <RollbackOutlined />,
redo: <RollbackOutlined style={{ transform: 'scaleX(-1)' }} />,
min: <ZoomOutOutlined />,
max: <ZoomInOutlined />,
full: <BorderOutlined />,
miniMap: <PictureFilled />,
miniMapNo: <PictureOutlined />,
dagreLayout: <PartitionOutlined />,
relationLayout: <UngroupOutlined />,
reload: <ReloadOutlined />,
image: <DownloadOutlined />,
darkness: <SnippetsFilled />,
light: <SnippetsOutlined />,
colorClose: <BgColorsOutlined />,
colorOpen: <BgColorsOutlined />
}
Example #7
Source File: PrintButton.tsx From next-basics with GNU General Public License v3.0 | 5 votes |
export function PrintButton(props: PrintButtonProps): React.ReactElement {
const [visible, setVisible] = useState(false);
const content = (
<span>
查看{" "}
<a onClick={preRemind}>
帮助{" "}
<InfoCircleOutlined
style={{ transform: "translateY(1px)" }}
></InfoCircleOutlined>
</a>
</span>
);
const handleVisible = (isVisible: boolean) => {
setVisible(isVisible);
};
function preRemind() {
const padding = 32;
const pngWidth = 800;
Modal.info({
className: styles.printModalBody,
maskClosable: true,
title: "打印操作指引",
width: pngWidth + padding * 2,
okText: "关闭",
content: (
<div style={{ overflowY: "scroll", width: "100%", height: "500px" }}>
<img src={printHelperUrl} alt="打印操作指导图片" />
</div>
),
});
}
function invokePrint() {
const originTitle = window.parent.document.title;
// in iframe, maybe
window.parent.document.title =
props.prefixTitle +
"-" +
new Date().toISOString().substr(0, 10).replace(/-/g, "");
window.print();
window.parent.document.title = originTitle;
}
return (
<div>
<Popover
title={null}
content={content}
visible={visible}
onVisibleChange={handleVisible}
overlayClassName="print-hide"
placement="left"
>
<Button
className="print-hide"
icon={<DownloadOutlined />}
style={{
backgroundColor: props.backgroundColor,
color: props.color,
border: props.border,
width: props.size || 24,
height: props.size || 24,
}}
onClick={invokePrint}
></Button>
</Popover>
</div>
);
}
Example #8
Source File: Index.tsx From condo with MIT License | 5 votes |
ColumnsInfoBox: React.FC<IColumnsInfoBoxProps> = ({ columns, domainTranslate, exampleTemplateLink }) => {
const intl = useIntl()
const ColumnsFormatMessage = intl.formatMessage({ id: 'ImportRequiredColumnsFormat' }, {
domain: domainTranslate,
})
const RequiredFieldsMessage = intl.formatMessage({ id: 'ImportRequiredFields' })
const DownloadExampleTitle = intl.formatMessage({ id: 'ImportDownloadExampleTitle' })
const downloadExample = useCallback(
(event) => {
event.stopPropagation()
const link = document.createElement('a')
link.href = exampleTemplateLink
link.target = '_blank'
link.hidden = true
document.body.appendChild(link)
link.click()
link.parentNode.removeChild(link)
},
[exampleTemplateLink],
)
const fieldsString = columns.filter(({ required }) => required).map(column => column.label).join(', ')
return (
<Space direction={'vertical'} size={10} style={{ maxWidth: 300 }}>
<Typography.Text>
{ColumnsFormatMessage}
</Typography.Text>
<Typography.Text>{RequiredFieldsMessage}: {fieldsString}</Typography.Text>
{
exampleTemplateLink !== null && (
<Button
onClick={downloadExample}
icon={<DownloadOutlined />}
type={'inlineLink'}
>{DownloadExampleTitle}</Button>
)
}
</Space>
)
}
Example #9
Source File: TitleForShare.tsx From datart with Apache License 2.0 | 5 votes |
TitleForShare: FC<TitleHeaderProps> = memo(
({ name, onShareDownloadData, loadVizData, children }) => {
const t = useI18NPrefix(`viz.action`);
const [showTitle, setShowTitle] = useState<boolean>(false);
if (!showTitle) {
return (
<FixedButton onClick={() => setShowTitle(true)}>
<MoreOutlined />
</FixedButton>
);
}
return (
<Wrapper>
<Space>
{children}
<Popconfirm
placement="left"
title={t('common.confirm')}
okText={t('common.ok')}
cancelText={t('common.cancel')}
onConfirm={() => {
onShareDownloadData?.();
}}
>
<Tooltip title={t('share.downloadData')} placement="bottom">
<DownloadOutlined style={{ fontSize: 20 }} />
</Tooltip>
</Popconfirm>
<Tooltip title={t('syncData')} placement="bottom">
<ReloadOutlined style={IconStyle} onClick={loadVizData} />
</Tooltip>
<CloseOutlined
onClick={() => setShowTitle(false)}
style={IconStyle}
/>
</Space>
</Wrapper>
);
},
)
Example #10
Source File: index.tsx From visual-layout with MIT License | 5 votes |
Code: React.FC<{ project: ProjectService }> = ({ project }) => {
const [codeConfig, setCodeConfig] = useState<CodeConfig>(
getInitCodeConfig(project),
);
return (
<Visible>
{({ visible, setVisible }) => (
<>
<Drawer
title="代码"
placement="right"
width={'calc(100% - 420px)'}
onClose={() => setVisible(false)}
visible={visible}
destroyOnClose
>
<div className={styles.container}>
<div className={styles.leftContainer}>
<div className={styles.download}>
<Button
type="primary"
shape="round"
icon={<DownloadOutlined />}
onClick={() => exportCode(project, codeConfig)}
>
导出
</Button>
</div>
<BaseInfo project={project} />
<Config setCodeConfig={setCodeConfig} codeConfig={codeConfig} />
</div>
<div className={styles.rightContainer}>
<CodeEdit project={project} codeConfig={codeConfig} />
</div>
</div>
</Drawer>
<Tooltip placement="top" title="代码">
<CodeOutlined
style={{ fontSize: 20 }}
onClick={() => setVisible(true)}
/>
</Tooltip>
</>
)}
</Visible>
);
}
Example #11
Source File: Args.tsx From subscan-multisig-react with Apache License 2.0 | 5 votes |
export function Args({ args, className, section, method }: ArgsProps) {
const { t } = useTranslation();
const { chain } = useApi();
const columns: ColumnsType<ArgObj> = [
{
key: 'name',
dataIndex: 'name',
render(name: string, record) {
return name || record.type;
},
},
{
key: 'value',
dataIndex: 'value',
className: 'value-column',
// eslint-disable-next-line complexity
render(value, record) {
const { type, name } = record;
const isAddr = type ? isAddressType(type) : isSS58Address(value);
if (isObject(value)) {
return (
<Args
args={Object.entries(value).map(([prop, propValue]) => ({ name: prop, value: propValue }))}
section={section}
method={method}
/>
);
// return JSON.stringify(value);
}
if (isAddr) {
return formatAddressValue(value, chain);
}
// balances(transfer) kton(transfer)
if (isBalanceType(type || name) || isCrabValue(name) || section === 'balances' || method === 'transfer') {
const formatValue = toString(value).replaceAll(',', '');
return formatBalance(formatValue, +chain.tokens[0].decimal, {
noDecimal: false,
withThousandSplit: true,
}); // FIXME: decimal issue;
}
if (isDownloadType(value)) {
return (
<a href={value}>
{t('download')} <DownloadOutlined />
</a>
);
}
if (isValueType(name)) {
return value;
}
return <div style={{ wordBreak: 'break-all' }}>{value}</div>;
},
},
];
return (
<Table
columns={columns}
/* antd form data source require object array */
dataSource={args.map((arg) => (isObject(arg) ? arg : { value: arg }))}
pagination={false}
bordered
rowKey="name"
showHeader={false}
className={className}
/>
);
}
Example #12
Source File: MessageDetails.tsx From wildduck-ui with MIT License | 4 votes |
MessageDetails: React.FC = () => {
const { id }: any = useParams();
const { mailboxId, mailboxName } = useValues(mailboxesLogic);
const { messageId, attachmentId } = useValues(messagesLogic);
const attachment = useDownloadAttachment({
userId: id,
attachment: attachmentId,
mailboxId: mailboxId,
messageId: messageId,
});
const { data, isLoading, isError } = useMessageDetails({ userId: id, mailboxId: mailboxId, messageId: messageId });
const { setMessageDetailsToggle, setAttachmentId } = useActions(messagesLogic);
const { setUpdateMailboxToggle, setShowMailboxMessagesTable } = useActions(mailboxesLogic);
const pageBreadcrumb = (
<Breadcrumb>
<Breadcrumb.Item>
<a
onClick={() => {
setUpdateMailboxToggle(false);
setShowMailboxMessagesTable(false);
}}
>
Mailboxes
</a>
</Breadcrumb.Item>
<Breadcrumb.Item>
<a
onClick={(event) => {
event.stopPropagation();
setMessageDetailsToggle(false);
}}
>
{mailboxName}
</a>
</Breadcrumb.Item>
<Breadcrumb.Item>Message</Breadcrumb.Item>
</Breadcrumb>
);
const MessageTitle = (
<div>
<p>From : {`${_.get(data, 'from.name', '')}<${_.get(data, 'from.address', '')}>`}</p>
<p>To : {`${_.get(data, 'to.0.name', '')}<${_.get(data, 'to.0.address', '')}>`}</p>
<p>Subject : {_.get(data, 'subject', '')}</p>
</div>
);
return (
<Page title={pageBreadcrumb} loading={isLoading} error={isError}>
{_.isUndefined(data) ? null : (
<Card title={MessageTitle} extra={<>{moment(data.date).format(DATE_TIME_FORMAT_AP)}</>}>
<BraftEditor language='en' controls={[]} value={BraftEditor.createEditorState(data.html[0])} />
{_.isEmpty(data.attachments) ? null : (
<Descriptions size='small' bordered column={1}>
<Descriptions.Item label='Attachments'>
{_.map(data.attachments, (attachment, index) => {
return (
<Row key={index}>
<Col>
<p>
Filename: {_.get(attachment, 'filename', '')}
<br />
Content Type: {_.get(attachment, 'contentType', '')}
<br />
Size: {_.get(attachment, 'sizeKb', '')} KB
</p>
</Col>
<Col offset={1}>
<Tooltip title={'Download file'}>
<Button
className='ant-btn-icon'
shape='circle'
onClick={() => {
setAttachmentId(attachment);
}}
>
<DownloadOutlined className={'blue-color'} />
</Button>
</Tooltip>
</Col>
</Row>
);
})}
</Descriptions.Item>
</Descriptions>
)}
</Card>
)}
</Page>
);
}
Example #13
Source File: YakitStorePage.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
YakitStorePage: React.FC<YakitStorePageProp> = (props) => {
const [script, setScript] = useState<YakScript>()
const [trigger, setTrigger] = useState(false)
const [keyword, setKeyword] = useState("")
const [ignored, setIgnored] = useState(false)
const [history, setHistory] = useState(false)
const refresh = useMemoizedFn(() => {
setTrigger(!trigger)
})
const [loading, setLoading] = useState(false)
const [localPluginDir, setLocalPluginDir] = useState("");
const [pluginType, setPluginType] = useState<"yak" | "mitm" | "nuclei" | "codec" | "packet-hack" | "port-scan">("yak");
useEffect(() => {
getValue(YAKIT_DEFAULT_LOAD_LOCAL_PATH).then(e => {
if (e) {
setLocalPluginDir(`${e}`)
}
})
}, [])
useEffect(() => {
setLoading(true)
setTimeout(() => {
setLoading(false)
}, 200)
}, [script])
return (
<div style={{height: "100%", display: "flex", flexDirection: "row"}}>
<div style={{width: 470}}>
<AutoCard
bodyStyle={{padding: 0, overflow: "auto"}}
bordered={false}
style={{height: "100%"}}
title={
<Space size={0}>
类型:
<ManySelectOne
size={"small"}
data={[
{text: "YAK 插件", value: "yak"},
{text: "MITM 插件", value: "mitm"},
{text: "数据包扫描", value: "packet-hack"},
{text: "端口扫描插件", value: "port-scan"},
{text: "CODEC插件", value: "codec"},
{text: "YAML POC", value: "nuclei"},
]}
value={pluginType} setValue={setPluginType}
formItemStyle={{marginBottom: 0, width: 115}}
/>
<Button size={"small"} type={"link"} onClick={(e) => setTrigger(!trigger)}>
<ReloadOutlined/>
</Button>
<Button
size={"small"}
type={"link"}
onClick={(e) => {
let m = showModal({
title: "设置 Keyword",
content: (
<>
<KeywordSetter
defaultIgnore={ignored}
defaultHistory={history}
onFinished={(e, ignored, history) => {
setKeyword(e)
setIgnored(ignored)
setHistory(history)
m.destroy()
}}
defaultValue={keyword}
/>
</>
)
})
}}
>
<SearchOutlined/>
</Button>
</Space>
}
size={"small"}
extra={
<Space size={1}>
<Popover
title={"额外设置"}
content={<>
<Form onSubmitCapture={e => {
e.preventDefault()
saveValue(YAKIT_DEFAULT_LOAD_LOCAL_PATH, localPluginDir)
}} size={"small"}>
<InputItem
label={"本地插件仓库"} value={localPluginDir} setValue={setLocalPluginDir}
extraFormItemProps={{style: {marginBottom: 4}}}
/>
<Form.Item colon={false} label={" "} style={{marginBottom: 12}}>
<Button type="primary" htmlType="submit"> 设置 </Button>
</Form.Item>
</Form>
</>}
trigger={["click"]}
>
<Button
icon={<SettingOutlined/>} size={"small"} type={"link"}
style={{marginRight: 3}}
/>
</Popover>
<Button
size={"small"}
type={"primary"}
icon={<DownloadOutlined/>}
onClick={() => {
showModal({
width: 800,
title: "导入插件方式",
content: (
<>
<div style={{width: 800}}>
<LoadYakitPluginForm onFinished={refresh}/>
</div>
</>
)
})
}}
>
导入
</Button>
{/*<Popconfirm*/}
{/* title={"更新模块数据库?"}*/}
{/* onConfirm={e => {*/}
{/* showModal({*/}
{/* title: "自动更新 Yak 模块", content: <>*/}
{/* <AutoUpdateYakModuleViewer/>*/}
{/* </>, width: "60%",*/}
{/* })*/}
{/* }}*/}
{/*>*/}
{/* <Button size={"small"} type={"primary"} icon={<DownloadOutlined/>}>*/}
{/* 导入*/}
{/* </Button>*/}
{/*</Popconfirm>*/}
<Button
size={"small"}
type={"link"}
icon={<PlusOutlined/>}
onClick={() => {
let m = showDrawer({
title: "创建新插件",
width: "100%",
content: (
<>
<YakScriptCreatorForm
onChanged={(e) => setTrigger(!trigger)}
onCreated={() => {
m.destroy()
}}
/>
</>
),
keyboard: false
})
}}
>
新插件
</Button>
</Space>
}
>
<YakModuleList
currentId={script?.Id}
Keyword={keyword}
Type={pluginType}
onClicked={setScript}
trigger={trigger}
isHistory={history}
isIgnored={ignored}
/>
{/*<Tabs*/}
{/* className='yak-store-list'*/}
{/* tabPosition={"left"}*/}
{/* size={"small"}*/}
{/* tabBarStyle={{*/}
{/* padding: 0,*/}
{/* margin: 0,*/}
{/* width: 70,*/}
{/* marginLeft: -20*/}
{/* }}*/}
{/* direction={"ltr"}*/}
{/*>*/}
{/* {[*/}
{/* {tab: "YAK", key: "yak"},*/}
{/* {tab: "YAML", key: "nuclei"},*/}
{/* {tab: "MITM", key: "mitm"},*/}
{/* {tab: "Packet", key: "packet-hack"},*/}
{/* {tab: "CODEC", key: "codec"},*/}
{/* ].map((e) => {*/}
{/* return (*/}
{/* <Tabs.TabPane tab={e.tab} key={e.key}>*/}
{/* <YakModuleList*/}
{/* currentId={script?.Id}*/}
{/* Keyword={keyword}*/}
{/* Type={e.key as any}*/}
{/* onClicked={setScript}*/}
{/* trigger={trigger}*/}
{/* isHistory={history}*/}
{/* isIgnored={ignored}*/}
{/* />*/}
{/* </Tabs.TabPane>*/}
{/* )*/}
{/* })}*/}
{/*</Tabs>*/}
</AutoCard>
</div>
<div style={{flex: 1, overflowX: "hidden"}}>
{script ? (
<AutoCard
loading={loading}
title={
<Space>
<div>Yak[{script?.Type}] 模块详情</div>
</Space>
}
bordered={false}
size={"small"}
>
<PluginOperator yakScriptId={script.Id} setTrigger={() => setTrigger(!trigger)}
setScript={setScript}/>
</AutoCard>
) : (
<Empty style={{marginTop: 100}}>在左侧所选模块查看详情</Empty>
)}
</div>
</div>
)
}
Example #14
Source File: FileManager.tsx From anew-server with MIT License | 4 votes |
FileManager: React.FC<FileManagerProps> = (props) => {
const { modalVisible, handleChange, connectId } = props;
const [columnData, setColumnData] = useState<API.SSHFileList[]>([]);
const [showHidden, setShowHidden] = useState<boolean>(false);
const [childrenDrawer, setChildrenDrawer] = useState<boolean>(false);
const [currentPathArr, setCurrentPathArr] = useState<string[]>([]);
const [initPath, setInitPath] = useState<string>('');
const _dirSort = (item: API.SSHFileList) => {
return item.isDir;
};
const getFileData = (key: string, path: string) => {
querySSHFile(key, path).then((res) => {
const obj = lds.orderBy(res.data, [_dirSort, 'name'], ['desc', 'asc']);
showHidden ? setColumnData(obj) : setColumnData(obj.filter((x) => !x.name.startsWith('.')));
try {
// 获取服务器的当前路径
let pathb = obj[0].path;
const index = pathb.lastIndexOf('/');
pathb = pathb.substring(0, index + 1);
setCurrentPathArr(pathb.split('/').filter((x: any) => x !== ''));
setInitPath(pathb); // 保存当前路径,刷新用
} catch (exception) {
setCurrentPathArr(path.split('/').filter((x) => x !== ''));
setInitPath(path);
}
});
};
const getChdirDirData = (key: string, path: string) => {
const index = currentPathArr.indexOf(path);
const currentDir = '/' + currentPathArr.splice(0, index + 1).join('/');
getFileData(key, currentDir);
};
const handleDelete = (key: string, path: string) => {
if (!path) return;
const index = path.lastIndexOf('/');
const currentDir = path.substring(0, index + 1);
const currentFile = path.substring(index + 1, path.length);
const content = `您是否要删除 ${currentFile}?`;
Modal.confirm({
title: '注意',
content,
onOk: () => {
deleteSSHFile(key, path).then((res) => {
if (res.code === 200 && res.status === true) {
message.success(res.message);
getFileData(key, currentDir);
}
});
},
onCancel() { },
});
};
const handleDownload = (key: string, path: string) => {
if (!path) return;
const index = path.lastIndexOf('/');
const currentFile = path.substring(index + 1, path.length);
const content = `您是否要下载 ${currentFile}?`;
Modal.confirm({
title: '注意',
content,
onOk: () => {
const token = localStorage.getItem('token');
const link = document.createElement('a');
link.href = `/api/v1/host/ssh/download?key=${key}&path=${path}&token=${token}`;
document.body.appendChild(link);
const evt = document.createEvent('MouseEvents');
evt.initEvent('click', false, false);
link.dispatchEvent(evt);
document.body.removeChild(link);
},
onCancel() { },
});
};
const uploadProps = {
name: 'file',
action: `/api/v1/host/ssh/upload?key=${connectId}&path=${initPath}`,
multiple: true,
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
// showUploadList: {
// removeIcon: false,
// showRemoveIcon: false,
// },
onChange(info: any) {
// if (info.file.status !== 'uploading') {
// console.log(info.file, info.fileList);
// }
//console.log(info);
if (info.file.status === 'done') {
message.success(`${info.file.name} file uploaded successfully`);
getFileData(connectId, initPath as string); // 刷新数据
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
},
progress: {
strokeColor: {
'0%': '#108ee9',
'100%': '#87d068',
},
strokeWidth: 3,
format: (percent: any) => `${parseFloat(percent.toFixed(2))}%`,
},
};
const columns: ProColumns<API.SSHFileList>[] = [
{
title: '名称',
dataIndex: 'name',
render: (_, record) =>
record.isDir ? (
<div onClick={() => getFileData(connectId, record.path)} style={{ cursor: 'pointer' }}>
<FolderTwoTone />
<span style={{ color: '#1890ff', paddingLeft: 5 }}>{record.name}</span>
</div>
) : (
<React.Fragment>
{record.isLink ? (
<div>
<LinkOutlined />
<Tooltip title="Is Link">
<span style={{ color: '#3cb371', paddingLeft: 5 }}>{record.name}</span>
</Tooltip>
</div>
) : (
<div>
<FileOutlined />
<span style={{ paddingLeft: 5 }}>{record.name}</span>
</div>
)}
</React.Fragment>
),
},
{
title: '大小',
dataIndex: 'size',
},
{
title: '修改时间',
dataIndex: 'mtime',
},
{
title: '属性',
dataIndex: 'mode',
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) =>
!record.isDir && !record.isLink ? (
<>
<Tooltip title="下载文件">
<DownloadOutlined
style={{ fontSize: '17px', color: 'blue' }}
onClick={() => handleDownload(connectId, record.path)}
/>
</Tooltip>
<Divider type="vertical" />
<Tooltip title="删除文件">
<DeleteOutlined
style={{ fontSize: '17px', color: 'red' }}
onClick={() => handleDelete(connectId, record.path)}
/>
</Tooltip>
</>
) : null,
},
];
useEffect(() => {
// 是否显示隐藏文件
getFileData(connectId, initPath as string); // 刷新数据
}, [showHidden]);
const { Dragger } = Upload;
return (
<Drawer
title="文件管理器"
placement="right"
width={800}
visible={modalVisible}
onClose={()=>handleChange(false)}
getContainer={false}
>
{/* <input style={{ display: 'none' }} type="file" ref={(ref) => (this.input = ref)} /> */}
<div className={styles.drawerHeader}>
<Breadcrumb>
<Breadcrumb.Item href="#" onClick={() => getFileData(connectId, '/')}>
<ApartmentOutlined />
</Breadcrumb.Item>
<Breadcrumb.Item href="#" onClick={() => getFileData(connectId, '')}>
<HomeOutlined />
</Breadcrumb.Item>
{currentPathArr.map((item) => (
<Breadcrumb.Item key={item} href="#" onClick={() => getChdirDirData(connectId, item)}>
<span>{item}</span>
</Breadcrumb.Item>
))}
</Breadcrumb>
<div style={{ display: 'flex', alignItems: 'center' }}>
<span>显示隐藏文件:</span>
<Switch
checked={showHidden}
checkedChildren="开启"
unCheckedChildren="关闭"
onChange={(v) => {
setShowHidden(v);
}}
/>
<Button
style={{ marginLeft: 10 }}
size="small"
type="primary"
icon={<UploadOutlined />}
onClick={() => setChildrenDrawer(true)}
>
上传文件
</Button>
</div>
</div>
<Drawer
title="上传文件"
width={320}
closable={false}
onClose={() => setChildrenDrawer(false)}
visible={childrenDrawer}
>
<div style={{ height: 150 }}>
<Dragger {...uploadProps}>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">单击或拖入上传</p>
<p className="ant-upload-hint">支持多文件</p>
</Dragger>
</div>
</Drawer>
<ProTable
pagination={false}
search={false}
toolBarRender={false}
rowKey="name"
dataSource={columnData}
columns={columns}
/>
</Drawer>
);
}
Example #15
Source File: ChartHeaderPanel.tsx From datart with Apache License 2.0 | 4 votes |
ChartHeaderPanel: FC<{
chartName?: string;
orgId?: string;
container?: string;
onSaveChart?: () => void;
onGoBack?: () => void;
onSaveChartToDashBoard?: (dashboardId, dashboardType) => void;
}> = memo(
({
chartName,
orgId,
container,
onSaveChart,
onGoBack,
onSaveChartToDashBoard,
}) => {
const t = useI18NPrefix(`viz.workbench.header`);
const hasVizFetched = useSelector(selectHasVizFetched);
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
const backendChart = useSelector(backendChartSelector);
const downloadPolling = useSelector(selectChartEditorDownloadPolling);
const dispatch = useDispatch();
const { actions } = useWorkbenchSlice();
const handleModalOk = useCallback(
(dashboardId: string, dashboardType: string) => {
onSaveChartToDashBoard?.(dashboardId, dashboardType);
setIsModalVisible(true);
},
[onSaveChartToDashBoard],
);
const handleModalCancel = useCallback(() => {
setIsModalVisible(false);
}, []);
const onSetPolling = useCallback(
(polling: boolean) => {
dispatch(actions.setChartEditorDownloadPolling(polling));
},
[dispatch, actions],
);
useMount(() => {
if (!hasVizFetched) {
// Request data when there is no data
dispatch(getFolders(orgId as string));
}
});
return (
<Wrapper>
<h1>{chartName}</h1>
<Space>
<DownloadListPopup
polling={downloadPolling}
setPolling={onSetPolling}
onLoadTasks={loadTasks}
onDownloadFile={item => {
if (item.id) {
downloadFile(item.id).then(() => {
dispatch(actions.setChartEditorDownloadPolling(true));
});
}
}}
renderDom={
<Button icon={<DownloadOutlined />}>{t('downloadList')}</Button>
}
/>
<Button onClick={onGoBack}>{t('cancel')}</Button>
<Button type="primary" onClick={onSaveChart}>
{t('save')}
</Button>
{!(container === 'widget') && (
<Button
type="primary"
onClick={() => {
setIsModalVisible(true);
}}
>
{t('saveToDashboard')}
</Button>
)}
<SaveToDashboard
orgId={orgId as string}
title={t('saveToDashboard')}
isModalVisible={isModalVisible}
handleOk={handleModalOk}
handleCancel={handleModalCancel}
backendChartId={backendChart?.id}
></SaveToDashboard>
</Space>
</Wrapper>
);
},
)
Example #16
Source File: HTTPFuzzerPage.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
HTTPFuzzerPage: React.FC<HTTPFuzzerPageProp> = (props) => {
// params
const [isHttps, setIsHttps, getIsHttps] = useGetState<boolean>(props.fuzzerParams?.isHttps || props.isHttps || false)
const [noFixContentLength, setNoFixContentLength] = useState(false)
const [request, setRequest, getRequest] = useGetState(props.fuzzerParams?.request || props.request || defaultPostTemplate)
const [concurrent, setConcurrent] = useState(props.fuzzerParams?.concurrent || 20)
const [forceFuzz, setForceFuzz] = useState<boolean>(props.fuzzerParams?.forceFuzz || true)
const [timeout, setParamTimeout] = useState(props.fuzzerParams?.timeout || 10.0)
const [proxy, setProxy] = useState(props.fuzzerParams?.proxy || "")
const [actualHost, setActualHost] = useState(props.fuzzerParams?.actualHost || "")
const [advancedConfig, setAdvancedConfig] = useState(false)
const [redirectedResponse, setRedirectedResponse] = useState<FuzzerResponse>()
const [historyTask, setHistoryTask] = useState<HistoryHTTPFuzzerTask>();
const [hotPatchCode, setHotPatchCode] = useState<string>("");
// filter
const [_, setFilter, getFilter] = useGetState<FuzzResponseFilter>({
Keywords: [],
MaxBodySize: 0,
MinBodySize: 0,
Regexps: [],
StatusCode: []
});
const [droppedCount, setDroppedCount] = useState(0);
// state
const [loading, setLoading] = useState(false)
const [content, setContent] = useState<FuzzerResponse[]>([])
const [reqEditor, setReqEditor] = useState<IMonacoEditor>()
const [fuzzToken, setFuzzToken] = useState("")
const [search, setSearch] = useState("")
const [targetUrl, setTargetUrl] = useState("")
const [refreshTrigger, setRefreshTrigger] = useState(false)
const refreshRequest = () => {
setRefreshTrigger(!refreshTrigger)
}
// history
const [history, setHistory] = useState<string[]>([])
const [currentHistoryIndex, setCurrentHistoryIndex] = useState<number>()
const [urlPacketShow, setUrlPacketShow] = useState<boolean>(false)
// filter
const [keyword, setKeyword] = useState<string>("")
const [filterContent, setFilterContent] = useState<FuzzerResponse[]>([])
const [timer, setTimer] = useState<any>()
useEffect(() => {
getValue(WEB_FUZZ_HOTPATCH_CODE).then((data: any) => {
if (!data) {
return
}
setHotPatchCode(`${data}`)
})
}, [])
// 定时器
const sendTimer = useRef<any>(null)
const withdrawRequest = useMemoizedFn(() => {
const targetIndex = history.indexOf(request) - 1
if (targetIndex >= 0) {
setRequest(history[targetIndex])
setCurrentHistoryIndex(targetIndex)
}
})
const forwardRequest = useMemoizedFn(() => {
const targetIndex = history.indexOf(request) + 1
if (targetIndex < history.length) {
setCurrentHistoryIndex(targetIndex)
setRequest(history[targetIndex])
}
})
const sendToFuzzer = useMemoizedFn((isHttps: boolean, request: string) => {
ipcRenderer.invoke("send-to-tab", {
type: "fuzzer",
data: {isHttps: isHttps, request: request}
})
})
const sendToPlugin = useMemoizedFn((request: Uint8Array, isHTTPS: boolean, response?: Uint8Array) => {
let m = showDrawer({
width: "80%",
content: <HackerPlugin request={request} isHTTPS={isHTTPS} response={response}></HackerPlugin>
})
})
// 从历史记录中恢复
useEffect(() => {
if (!historyTask) {
return
}
setRequest(historyTask.Request)
setIsHttps(historyTask.IsHTTPS)
setProxy(historyTask.Proxy)
refreshRequest()
}, [historyTask])
useEffect(() => {
// 缓存全局参数
getValue(WEB_FUZZ_PROXY).then(e => {
if (!e) {
return
}
setProxy(`${e}`)
})
}, [])
useEffect(() => {
if (currentHistoryIndex === undefined) {
return
}
refreshRequest()
}, [currentHistoryIndex])
useEffect(() => {
setIsHttps(!!props.isHttps)
if (props.request) {
setRequest(props.request)
setContent([])
}
}, [props.isHttps, props.request])
const loadHistory = useMemoizedFn((id: number) => {
setLoading(true)
setHistory([])
ipcRenderer.invoke(
"HTTPFuzzer",
{HistoryWebFuzzerId: id}, fuzzToken
).then(() => {
ipcRenderer.invoke("GetHistoryHTTPFuzzerTask", {Id: id}).then((data: { OriginRequest: HistoryHTTPFuzzerTask }) => {
setHistoryTask(data.OriginRequest)
})
})
})
const submitToHTTPFuzzer = useMemoizedFn(() => {
// 清楚历史任务的标记
setHistoryTask(undefined);
saveValue(WEB_FUZZ_PROXY, proxy)
setLoading(true)
if (history.includes(request)) {
history.splice(history.indexOf(request), 1)
}
history.push(request)
setHistory([...history])
setDroppedCount(0)
ipcRenderer.invoke(
"HTTPFuzzer",
{
Request: request,
ForceFuzz: forceFuzz,
IsHTTPS: isHttps,
Concurrent: concurrent,
PerRequestTimeoutSeconds: timeout,
NoFixContentLength: noFixContentLength,
Proxy: proxy,
ActualAddr: actualHost,
HotPatchCode: hotPatchCode,
Filter: getFilter(),
},
fuzzToken
)
})
const cancelCurrentHTTPFuzzer = useMemoizedFn(() => {
ipcRenderer.invoke("cancel-HTTPFuzzer", fuzzToken)
})
useEffect(() => {
const token = randomString(60)
setFuzzToken(token)
const dataToken = `${token}-data`
const errToken = `${token}-error`
const endToken = `${token}-end`
ipcRenderer.on(errToken, (e, details) => {
notification["error"]({
message: `提交模糊测试请求失败 ${details}`,
placement: "bottomRight"
})
})
let buffer: FuzzerResponse[] = []
let droppedCount = 0;
let count: number = 0
const updateData = () => {
if (buffer.length <= 0) {
return
}
if (JSON.stringify(buffer) !== JSON.stringify(content)) setContent([...buffer])
}
ipcRenderer.on(dataToken, (e: any, data: any) => {
if (data["MatchedByFilter"] !== true && !filterIsEmpty(getFilter())) {
// 不匹配的
droppedCount++
setDroppedCount(droppedCount)
return
}
// const response = new Buffer(data.ResponseRaw).toString(fixEncoding(data.GuessResponseEncoding))
buffer.push({
StatusCode: data.StatusCode,
Ok: data.Ok,
Reason: data.Reason,
Method: data.Method,
Host: data.Host,
ContentType: data.ContentType,
Headers: (data.Headers || []).map((i: any) => {
return {Header: i.Header, Value: i.Value}
}),
DurationMs: data.DurationMs,
BodyLength: data.BodyLength,
UUID: data.UUID,
Timestamp: data.Timestamp,
ResponseRaw: data.ResponseRaw,
RequestRaw: data.RequestRaw,
Payloads: data.Payloads,
IsHTTPS: data.IsHTTPS,
Count: count,
BodySimilarity: data.BodySimilarity,
HeaderSimilarity: data.HeaderSimilarity,
} as FuzzerResponse)
count++
// setContent([...buffer])
})
ipcRenderer.on(endToken, () => {
updateData()
buffer = []
count = 0
droppedCount = 0
setLoading(false)
})
const updateDataId = setInterval(() => {
updateData()
}, 200)
return () => {
ipcRenderer.invoke("cancel-HTTPFuzzer", token)
clearInterval(updateDataId)
ipcRenderer.removeAllListeners(errToken)
ipcRenderer.removeAllListeners(dataToken)
ipcRenderer.removeAllListeners(endToken)
}
}, [])
const searchContent = (keyword: string) => {
if (timer) {
clearTimeout(timer)
setTimer(null)
}
setTimer(
setTimeout(() => {
try {
const filters = content.filter((item) => {
return Buffer.from(item.ResponseRaw).toString("utf8").match(new RegExp(keyword, "g"))
})
setFilterContent(filters)
} catch (error) {
}
}, 500)
)
}
const downloadContent = useMemoizedFn(() => {
if (!keyword) {
failed('请先输入需要搜索的关键词')
return
}
const strs = []
try {
const reg = new RegExp(keyword)
for (let info of filterContent) {
let str = Buffer.from(info.ResponseRaw).toString('utf8')
let temp: any
while ((temp = reg.exec(str)) !== null) {
// @ts-ignore
if (temp[1]) {
// @ts-ignore
strs.push(temp[1])
str = str.substring(temp['index'] + 1)
reg.lastIndex = 0
} else {
break
}
}
}
} catch (error) {
failed("正则有问题,请检查后重新输入")
return
}
if (strs.length === 0) {
failed('未捕获到关键词信息')
return
}
ipcRenderer.invoke("show-save-dialog", 'fuzzer列表命中内容.txt').then((res) => {
if (res.canceled) return
ipcRenderer
.invoke("write-file", {
route: res.filePath,
data: strs.join('\n\r')
})
.then(() => {
success('下载完成')
ipcRenderer.invoke("open-specified-file", res.filePath)
})
})
})
useEffect(() => {
if (!!keyword) {
searchContent(keyword)
} else {
setFilterContent([])
}
}, [keyword])
useEffect(() => {
if (keyword && content.length !== 0) {
const filters = content.filter(item => {
return Buffer.from(item.ResponseRaw).toString("utf8").match(new RegExp(keyword, 'g'))
})
setFilterContent(filters)
}
}, [content])
const onlyOneResponse = !loading && (content || []).length === 1
const filtredResponses =
search === ""
? content || []
: (content || []).filter((i) => {
return Buffer.from(i.ResponseRaw).toString().includes(search)
})
const successResults = filtredResponses.filter((i) => i.Ok)
const failedResults = filtredResponses.filter((i) => !i.Ok)
const sendFuzzerSettingInfo = useMemoizedFn(() => {
const info: fuzzerInfoProp = {
time: new Date().getTime().toString(),
isHttps: isHttps,
forceFuzz: forceFuzz,
concurrent: concurrent,
proxy: proxy,
actualHost: actualHost,
timeout: timeout,
request: request
}
if (sendTimer.current) {
clearTimeout(sendTimer.current)
sendTimer.current = null
}
sendTimer.current = setTimeout(() => {
ipcRenderer.invoke('send-fuzzer-setting-data', {key: props.order || "", param: JSON.stringify(info)})
}, 1000);
})
useEffect(() => {
sendFuzzerSettingInfo()
}, [isHttps, forceFuzz, concurrent, proxy, actualHost, timeout, request])
const responseViewer = useMemoizedFn((rsp: FuzzerResponse) => {
return (
<HTTPPacketEditor
system={props.system}
originValue={rsp.ResponseRaw}
bordered={true}
hideSearch={true}
emptyOr={
!rsp?.Ok && (
<Result
status={"error"}
title={"请求失败"}
// no such host
subTitle={(() => {
const reason = content[0]!.Reason
if (reason.includes("tcp: i/o timeout")) {
return "网络超时"
}
if (reason.includes("no such host")) {
return "DNS 错误或主机错误"
}
return undefined
})()}
>
<>详细原因:{rsp.Reason}</>
</Result>
)
}
readOnly={true}
extra={
(
<Space>
{loading && <Spin size={"small"} spinning={loading}/>}
{onlyOneResponse ? (
<Space>
{content[0].IsHTTPS && <Tag>{content[0].IsHTTPS ? "https" : ""}</Tag>}
<Tag>{content[0].DurationMs}ms</Tag>
<Space key='single'>
<Button
size={"small"}
onClick={() => {
analyzeFuzzerResponse(
rsp,
(bool, r) => {
// setRequest(r)
// refreshRequest()
}
)
}}
type={"primary"}
icon={<ProfileOutlined/>}
>
详情
</Button>
<Button
type={"primary"}
size={"small"}
onClick={() => {
setContent([])
}}
danger={true}
icon={<DeleteOutlined/>}
/>
</Space>
</Space>
) : (
<Space key='list'>
<Tag color={"green"}>成功:{successResults.length}</Tag>
<Input
size={"small"}
value={search}
onChange={(e) => {
setSearch(e.target.value)
}}
/>
{/*<Tag>当前请求结果数[{(content || []).length}]</Tag>*/}
<Button
size={"small"}
onClick={() => {
setContent([])
}}
>
清除数据
</Button>
</Space>
)}
</Space>
)
}
/>
)
})
const hotPatchTrigger = useMemoizedFn(() => {
let m = showModal({
title: "调试 / 插入热加载代码",
width: "60%",
content: (
<div>
<HTTPFuzzerHotPatch initialHotPatchCode={hotPatchCode || ""} onInsert={tag => {
if (reqEditor) monacoEditorWrite(reqEditor, tag);
m.destroy()
}} onSaveCode={code => {
setHotPatchCode(code)
saveValue(WEB_FUZZ_HOTPATCH_CODE, code)
}}/>
</div>
)
})
})
return (
<div style={{height: "100%", width: "100%", display: "flex", flexDirection: "column", overflow: "hidden"}}>
<Row gutter={8} style={{marginBottom: 8}}>
<Col span={24} style={{textAlign: "left", marginTop: 4}}>
<Space>
{loading ? (
<Button
style={{width: 150}}
onClick={() => {
cancelCurrentHTTPFuzzer()
}}
// size={"small"}
danger={true}
type={"primary"}
>
强制停止
</Button>
) : (
<Button
style={{width: 150}}
onClick={() => {
setContent([])
setRedirectedResponse(undefined)
sendFuzzerSettingInfo()
submitToHTTPFuzzer()
}}
// size={"small"}
type={"primary"}
>
发送数据包
</Button>
)}
<Space>
<Button
onClick={() => {
withdrawRequest()
}}
type={"link"}
icon={<LeftOutlined/>}
/>
<Button
onClick={() => {
forwardRequest()
}}
type={"link"}
icon={<RightOutlined/>}
/>
{history.length > 1 && (
<Dropdown
trigger={["click"]}
overlay={() => {
return (
<Menu>
{history.map((i, index) => {
return (
<Menu.Item
style={{width: 120}}
onClick={() => {
setRequest(i)
setCurrentHistoryIndex(index)
}}
>{`${index}`}</Menu.Item>
)
})}
</Menu>
)
}}
>
<Button size={"small"} type={"link"} onClick={(e) => e.preventDefault()}>
History <DownOutlined/>
</Button>
</Dropdown>
)}
</Space>
<Checkbox defaultChecked={isHttps} value={isHttps} onChange={() => setIsHttps(!isHttps)}>强制
HTTPS</Checkbox>
<SwitchItem
label={"高级配置"}
formItemStyle={{marginBottom: 0}}
value={advancedConfig}
setValue={setAdvancedConfig}
size={"small"}
/>
{droppedCount > 0 && <Tag color={"red"}>已丢弃[{droppedCount}]个响应</Tag>}
{onlyOneResponse && content[0].Ok && (
<Form.Item style={{marginBottom: 0}}>
<Button
onClick={() => {
setLoading(true)
ipcRenderer
.invoke("RedirectRequest", {
Request: request,
Response: new Buffer(content[0].ResponseRaw).toString("utf8"),
IsHttps: isHttps,
PerRequestTimeoutSeconds: timeout,
NoFixContentLength: noFixContentLength,
Proxy: proxy
})
.then((rsp: FuzzerResponse) => {
setRedirectedResponse(rsp)
})
.catch((e) => {
failed(`"ERROR in: ${e}"`)
})
.finally(() => {
setTimeout(() => setLoading(false), 300)
})
}}
>
跟随重定向
</Button>
</Form.Item>
)}
{loading && (
<Space>
<Spin size={"small"}/>
<div style={{color: "#3a8be3"}}>sending packets</div>
</Space>
)}
{proxy && <Tag>代理:{proxy}</Tag>}
{/*<Popover*/}
{/* trigger={"click"}*/}
{/* content={*/}
{/* }*/}
{/*>*/}
{/* <Button type={"link"} size={"small"}>*/}
{/* 配置请求包*/}
{/* </Button>*/}
{/*</Popover>*/}
{actualHost !== "" && <Tag color={"red"}>请求 Host:{actualHost}</Tag>}
</Space>
</Col>
{/*<Col span={12} style={{textAlign: "left"}}>*/}
{/*</Col>*/}
</Row>
{advancedConfig && (
<Row style={{marginBottom: 8}} gutter={8}>
<Col span={16}>
{/*高级配置*/}
<Card bordered={true} size={"small"} bodyStyle={{height: 106}}>
<Spin style={{width: "100%"}} spinning={!reqEditor}>
<Form
onSubmitCapture={(e) => e.preventDefault()}
// layout={"horizontal"}
size={"small"}
// labelCol={{span: 8}}
// wrapperCol={{span: 16}}
>
<Row gutter={8}>
<Col span={12} xl={8}>
<Form.Item
label={<OneLine width={68}>Intruder</OneLine>}
style={{marginBottom: 4}}
>
<Button
style={{backgroundColor: "#08a701"}}
size={"small"}
type={"primary"}
onClick={() => {
const m = showModal({
width: "70%",
content: (
<>
<StringFuzzer
advanced={true}
disableBasicMode={true}
insertCallback={(template: string) => {
if (!template) {
Modal.warn({
title: "Payload 为空 / Fuzz 模版为空"
})
} else {
if (reqEditor && template) {
reqEditor.trigger(
"keyboard",
"type",
{
text: template
}
)
} else {
Modal.error({
title: "BUG: 编辑器失效"
})
}
m.destroy()
}
}}
/>
</>
)
})
}}
>
插入 yak.fuzz 语法
</Button>
</Form.Item>
</Col>
<Col span={12} xl={8}>
<SwitchItem
label={<OneLine width={68}>渲染 fuzz</OneLine>}
setValue={(e) => {
if (!e) {
Modal.confirm({
title: "确认关闭 Fuzz 功能吗?关闭之后,所有的 Fuzz 标签将会失效",
onOk: () => {
setForceFuzz(e)
}
})
return
}
setForceFuzz(e)
}}
size={"small"}
value={forceFuzz}
formItemStyle={{marginBottom: 4}}
/>
</Col>
<Col span={12} xl={8}>
<InputInteger
label={<OneLine width={68}>并发线程</OneLine>}
size={"small"}
setValue={(e) => {
setConcurrent(e)
}}
formItemStyle={{marginBottom: 4}} // width={40}
width={50}
value={concurrent}
/>
</Col>
<Col span={12} xl={8}>
<SwitchItem
label={<OneLine width={68}>HTTPS</OneLine>}
setValue={(e) => {
setIsHttps(e)
}}
size={"small"}
value={isHttps}
formItemStyle={{marginBottom: 4}}
/>
</Col>
<Col span={12} xl={8}>
<SwitchItem
label={<OneLine width={70}>
<Tooltip title={"不修复 Content-Length: 常用发送多个数据包"}>
不修复长度
</Tooltip>
</OneLine>}
setValue={(e) => {
setNoFixContentLength(e)
}}
size={"small"}
value={noFixContentLength}
formItemStyle={{marginBottom: 4}}
/>
</Col>
<Col span={12} xl={8}>
<ItemSelects
item={{
style: {marginBottom: 4},
label: <OneLine width={68}>设置代理</OneLine>,
}}
select={{
style: {width: "100%"},
allowClear: true,
autoClearSearchValue: true,
maxTagTextLength: 8,
mode: "tags",
data: [
{text: "http://127.0.0.1:7890", value: "http://127.0.0.1:7890"},
{text: "http://127.0.0.1:8080", value: "http://127.0.0.1:8080"},
{text: "http://127.0.0.1:8082", value: "http://127.0.0.1:8082"}
],
value: proxy ? proxy.split(",") : [],
setValue: (value) => setProxy(value.join(",")),
maxTagCount: "responsive",
}}
></ItemSelects>
{/* <ManyMultiSelectForString
formItemStyle={{marginBottom: 4}}
label={<OneLine width={68}>设置代理</OneLine>}
data={[
"http://127.0.0.1:7890",
"http://127.0.0.1:8080",
"http://127.0.0.1:8082"
].map((i) => {
return {label: i, value: i}
})}
mode={"tags"}
defaultSep={","}
value={proxy}
setValue={(r) => {
setProxy(r.split(",").join(","))
}}
/> */}
</Col>
<Col span={12} xl={8}>
<InputItem
extraFormItemProps={{
style: {marginBottom: 0}
}}
label={<OneLine width={68}>请求 Host</OneLine>}
setValue={setActualHost}
value={actualHost}
/>
</Col>
<Col span={12} xl={8}>
<InputFloat
formItemStyle={{marginBottom: 4}}
size={"small"}
label={<OneLine width={68}>超时时间</OneLine>}
setValue={setParamTimeout}
value={timeout}
/>
</Col>
</Row>
</Form>
</Spin>
</Card>
</Col>
<Col span={8}>
<AutoCard title={<Tooltip title={"通过过滤匹配,丢弃无用数据包,保证界面性能!"}>
设置过滤器
</Tooltip>}
bordered={false} size={"small"} bodyStyle={{paddingTop: 4}}
style={{marginTop: 0, paddingTop: 0}}
>
<Form size={"small"} onSubmitCapture={e => e.preventDefault()}>
<Row gutter={20}>
<Col span={12}>
<InputItem
label={"状态码"} placeholder={"200,300-399"}
disable={loading}
value={getFilter().StatusCode.join(",")}
setValue={e => {
setFilter({...getFilter(), StatusCode: e.split(",").filter(i => !!i)})
}}
extraFormItemProps={{style: {marginBottom: 0}}}
/>
</Col>
<Col span={12}>
<InputItem
label={"关键字"} placeholder={"Login,登录成功"}
value={getFilter().Keywords.join(",")}
disable={loading}
setValue={e => {
setFilter({...getFilter(), Keywords: e.split(",").filter(i => !!i)})
}}
extraFormItemProps={{style: {marginBottom: 0}}}
/>
</Col>
<Col span={12}>
<InputItem
label={"正则"} placeholder={`Welcome\\s+\\w+!`}
value={getFilter().Regexps.join(",")}
disable={loading}
setValue={e => {
setFilter({...getFilter(), Regexps: e.split(",").filter(i => !!i)})
}}
extraFormItemProps={{style: {marginBottom: 0, marginTop: 2}}}
/>
</Col>
</Row>
</Form>
</AutoCard>
</Col>
</Row>
)}
{/*<Divider style={{marginTop: 6, marginBottom: 8, paddingTop: 0}}/>*/}
<ResizeBox
firstMinSize={350} secondMinSize={360}
style={{overflow: "hidden"}}
firstNode={<HTTPPacketEditor
system={props.system}
refreshTrigger={refreshTrigger}
hideSearch={true}
bordered={true}
originValue={new Buffer(request)}
actions={[
{
id: "packet-from-url",
label: "URL转数据包",
contextMenuGroupId: "1_urlPacket",
run: () => {
setUrlPacketShow(true)
}
},
{
id: "copy-as-url",
label: "复制为 URL",
contextMenuGroupId: "1_urlPacket",
run: () => {
copyAsUrl({Request: getRequest(), IsHTTPS: getIsHttps()})
}
},
{
id: "insert-intruder-tag",
label: "插入模糊测试字典标签",
contextMenuGroupId: "1_urlPacket",
run: (editor) => {
showDictsAndSelect(i => {
monacoEditorWrite(editor, i, editor.getSelection())
})
}
},
{
id: "insert-hotpatch-tag",
label: "插入热加载标签",
contextMenuGroupId: "1_urlPacket",
run: (editor) => {
hotPatchTrigger()
}
},
]}
onEditor={setReqEditor}
onChange={(i) => setRequest(new Buffer(i).toString("utf8"))}
extra={
<Space size={2}>
<Button
style={{marginRight: 1}}
size={"small"} type={"primary"}
onClick={() => {
hotPatchTrigger()
}}
>热加载标签</Button>
<Popover
trigger={"click"}
title={"从 URL 加载数据包"}
content={
<div style={{width: 400}}>
<Form
layout={"vertical"}
onSubmitCapture={(e) => {
e.preventDefault()
ipcRenderer
.invoke("Codec", {
Type: "packet-from-url",
Text: targetUrl
})
.then((e) => {
if (e?.Result) {
setRequest(e.Result)
refreshRequest()
}
})
.finally(() => {
})
}}
size={"small"}
>
<InputItem
label={"从 URL 构造请求"}
value={targetUrl}
setValue={setTargetUrl}
extraFormItemProps={{style: {marginBottom: 8}}}
></InputItem>
<Form.Item style={{marginBottom: 8}}>
<Button type={"primary"} htmlType={"submit"}>
构造请求
</Button>
</Form.Item>
</Form>
</div>
}
>
<Button size={"small"} type={"primary"}>
URL
</Button>
</Popover>
<Popover
trigger={"click"}
placement={"bottom"}
destroyTooltipOnHide={true}
content={
<div style={{width: 400}}>
<HTTPFuzzerHistorySelector onSelect={e => {
loadHistory(e)
}}/>
</div>
}
>
<Button size={"small"} type={"primary"} icon={<HistoryOutlined/>}>
历史
</Button>
</Popover>
</Space>
}
/>}
secondNode={<AutoSpin spinning={false}>
{onlyOneResponse ? (
<>{redirectedResponse ? responseViewer(redirectedResponse) : responseViewer(content[0])}</>
) : (
<>
{(content || []).length > 0 ? (
<HTTPFuzzerResultsCard
onSendToWebFuzzer={sendToFuzzer}
sendToPlugin={sendToPlugin}
setRequest={(r) => {
setRequest(r)
refreshRequest()
}}
extra={
<div>
<Input
value={keyword}
style={{maxWidth: 200}}
allowClear
placeholder="输入字符串或正则表达式"
onChange={e => setKeyword(e.target.value)}
addonAfter={
<DownloadOutlined style={{cursor: "pointer"}}
onClick={downloadContent}/>
}></Input>
</div>
}
failedResponses={failedResults}
successResponses={filterContent.length !== 0 ? filterContent : keyword ? [] : successResults}
/>
) : (
<Result
status={"info"}
title={"请在左边编辑并发送一个 HTTP 请求/模糊测试"}
subTitle={
"本栏结果针对模糊测试的多个 HTTP 请求结果展示做了优化,可以自动识别单个/多个请求的展示"
}
/>
)}
</>
)}
</AutoSpin>}/>
<Modal
visible={urlPacketShow}
title='从 URL 加载数据包'
onCancel={() => setUrlPacketShow(false)}
footer={null}
>
<Form
layout={"vertical"}
onSubmitCapture={(e) => {
e.preventDefault()
ipcRenderer
.invoke("Codec", {
Type: "packet-from-url",
Text: targetUrl
})
.then((e) => {
if (e?.Result) {
setRequest(e.Result)
refreshRequest()
setUrlPacketShow(false)
}
})
.finally(() => {
})
}}
size={"small"}
>
<InputItem
label={"从 URL 构造请求"}
value={targetUrl}
setValue={setTargetUrl}
extraFormItemProps={{style: {marginBottom: 8}}}
></InputItem>
<Form.Item style={{marginBottom: 8}}>
<Button type={"primary"} htmlType={"submit"}>
构造请求
</Button>
</Form.Item>
</Form>
</Modal>
</div>
)
}
Example #17
Source File: palette.tsx From jmix-frontend with Apache License 2.0 | 4 votes |
palette = () => (
<Palette>
<Category name="Text">
<Component name="Formatted Message">
<Variant>
<FormattedMessage />
</Variant>
</Component>
<Component name="Heading">
<Variant name="h1">
<Typography.Title></Typography.Title>
</Variant>
<Variant name="h2">
<Typography.Title level={2}></Typography.Title>
</Variant>
<Variant name="h3">
<Typography.Title level={3}></Typography.Title>
</Variant>
<Variant name="h4">
<Typography.Title level={4}></Typography.Title>
</Variant>
<Variant name="h5">
<Typography.Title level={5}></Typography.Title>
</Variant>
</Component>
<Component name="Text">
<Variant>
<Typography.Text></Typography.Text>
</Variant>
<Variant name="Secondary">
<Typography.Text type="secondary"></Typography.Text>
</Variant>
<Variant name="Success">
<Typography.Text type="success"></Typography.Text>
</Variant>
<Variant name="Warning">
<Typography.Text type="warning"></Typography.Text>
</Variant>
<Variant name="Danger">
<Typography.Text type="danger"></Typography.Text>
</Variant>
<Variant name="Disabled">
<Typography.Text disabled></Typography.Text>
</Variant>
</Component>
</Category>
<Category name="Layout">
<Component name="Divider">
<Variant>
<Divider />
</Variant>
</Component>
<Component name="Grid">
<Variant name="Simple Row">
<Row></Row>
</Variant>
<Variant name="Two columns">
<Row>
<Col span={12}></Col>
<Col span={12}></Col>
</Row>
</Variant>
<Variant name="Three columns">
<Row>
<Col span={8}></Col>
<Col span={8}></Col>
<Col span={8}></Col>
</Row>
</Variant>
</Component>
<Component name="Space">
<Variant>
<Space />
</Variant>
<Variant name="Small">
<Space size={"small"} />
</Variant>
<Variant name="Large">
<Space size={"large"} />
</Variant>
</Component>
</Category>
<Category name="Controls">
<Component name="Autocomplete">
<Variant>
<AutoComplete placeholder="input here" />
</Variant>
</Component>
<Component name="Button">
<Variant>
<Button></Button>
</Variant>
<Variant name="Primary">
<Button type="primary"></Button>
</Variant>
<Variant name="Link">
<Button type="link"></Button>
</Variant>
<Variant name="Dropdown">
<Dropdown
trigger={["click"]}
overlay={
<Menu>
<Menu.Item></Menu.Item>
<Menu.Item></Menu.Item>
<Menu.Item></Menu.Item>
</Menu>
}
>
<Button></Button>
</Dropdown>
</Variant>
</Component>
<Component name="Checkbox">
<Variant>
<Checkbox />
</Variant>
</Component>
<Component name="Switch">
<Variant>
<Switch />
</Variant>
</Component>
<Component name="Radio Group">
<Variant>
<Radio.Group>
<Radio value={1}>A</Radio>
<Radio value={2}>B</Radio>
<Radio value={3}>C</Radio>
<Radio value={4}>D</Radio>
</Radio.Group>
</Variant>
<Variant name="Button">
<Radio.Group>
<Radio.Button value={1}>A</Radio.Button>
<Radio.Button value={2}>B</Radio.Button>
<Radio.Button value={3}>C</Radio.Button>
<Radio.Button value={4}>D</Radio.Button>
</Radio.Group>
</Variant>
</Component>
<Component name="DatePicker">
<Variant>
<DatePicker />
</Variant>
<Variant name="Range">
<DatePicker.RangePicker />
</Variant>
</Component>
<Component name="TimePicker">
<Variant>
<TimePicker />
</Variant>
<Variant name="Range">
<TimePicker.RangePicker />
</Variant>
</Component>
<Component name="Input">
<Variant>
<Input />
</Variant>
<Variant name="Number">
<InputNumber />
</Variant>
</Component>
<Component name="Select">
<Variant>
<Select defaultValue="1">
<Select.Option value="1">1</Select.Option>
<Select.Option value="2">2</Select.Option>
</Select>
</Variant>
<Variant name="Multiple">
<Select defaultValue={["1"]} mode="multiple" allowClear>
<Select.Option value="1">1</Select.Option>
<Select.Option value="2">2</Select.Option>
</Select>
</Variant>
</Component>
<Component name="Link">
<Variant>
<Typography.Link href="" target="_blank"></Typography.Link>
</Variant>
</Component>
<Component name="Slider">
<Variant>
<Slider defaultValue={30} />
</Variant>
<Variant name="Range">
<Slider range defaultValue={[20, 50]} />
</Variant>
</Component>
</Category>
<Category name="Data Display">
<Component name="Field">
<Variant>
<Field
entityName={ENTITY_NAME}
disabled={readOnlyMode}
propertyName=""
formItemProps={{
style: { marginBottom: "12px" }
}}
/>
</Variant>
</Component>
<Component name="Card">
<Variant>
<Card />
</Variant>
<Variant name="With Title">
<Card>
<Card title="Card title">
<p>Card content</p>
</Card>
</Card>
</Variant>
<Variant name="My custom card">
<Card>
<Card title="Card title">
<p>Card content</p>
<Avatar />
</Card>
</Card>
</Variant>
</Component>
<Component name="Tabs">
<Variant>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="Tab 1" key="1">
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 2" key="2">
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 3" key="3">
Content of Tab Pane 3
</Tabs.TabPane>
</Tabs>
</Variant>
<Variant name="Tab Pane">
<Tabs.TabPane></Tabs.TabPane>
</Variant>
</Component>
<Component name="Collapse">
<Variant>
<Collapse defaultActiveKey="1">
<Collapse.Panel
header="This is panel header 1"
key="1"
></Collapse.Panel>
<Collapse.Panel
header="This is panel header 2"
key="2"
></Collapse.Panel>
<Collapse.Panel
header="This is panel header 3"
key="3"
></Collapse.Panel>
</Collapse>
</Variant>
</Component>
<Component name="Image">
<Variant>
<Image width={200} src="" />
</Variant>
</Component>
<Component name="Avatar">
<Variant>
<Avatar icon={<UserOutlined />} />
</Variant>
<Variant name="Image">
<Avatar src="https://joeschmoe.io/api/v1/random" />
</Variant>
</Component>
<Component name="Badge">
<Variant>
<Badge count={1}></Badge>
</Variant>
</Component>
<Component name="Statistic">
<Variant>
<Statistic title="Title" value={112893} />
</Variant>
</Component>
<Component name="Alert">
<Variant name="Success">
<Alert message="Text" type="success" />
</Variant>
<Variant name="Info">
<Alert message="Text" type="info" />
</Variant>
<Variant name="Warning">
<Alert message="Text" type="warning" />
</Variant>
<Variant name="Error">
<Alert message="Text" type="error" />
</Variant>
</Component>
<Component name="List">
<Variant>
<List
bordered
dataSource={[]}
renderItem={item => <List.Item></List.Item>}
/>
</Variant>
</Component>
</Category>
<Category name="Icons">
<Component name="Arrow">
<Variant name="Up">
<ArrowUpOutlined />
</Variant>
<Variant name="Down">
<ArrowDownOutlined />
</Variant>
<Variant name="Left">
<ArrowLeftOutlined />
</Variant>
<Variant name="Right">
<ArrowRightOutlined />
</Variant>
</Component>
<Component name="Question">
<Variant>
<QuestionOutlined />
</Variant>
<Variant name="Circle">
<QuestionCircleOutlined />
</Variant>
</Component>
<Component name="Plus">
<Variant>
<PlusOutlined />
</Variant>
<Variant name="Circle">
<PlusCircleOutlined />
</Variant>
</Component>
<Component name="Info">
<Variant>
<InfoOutlined />
</Variant>
<Variant name="Circle">
<InfoCircleOutlined />
</Variant>
</Component>
<Component name="Exclamation">
<Variant>
<ExclamationOutlined />
</Variant>
<Variant name="Circle">
<ExclamationCircleOutlined />
</Variant>
</Component>
<Component name="Close">
<Variant>
<CloseOutlined />
</Variant>
<Variant name="Circle">
<CloseCircleOutlined />
</Variant>
</Component>
<Component name="Check">
<Variant>
<CheckOutlined />
</Variant>
<Variant name="Circle">
<CheckCircleOutlined />
</Variant>
</Component>
<Component name="Edit">
<Variant>
<EditOutlined />
</Variant>
</Component>
<Component name="Copy">
<Variant>
<CopyOutlined />
</Variant>
</Component>
<Component name="Delete">
<Variant>
<DeleteOutlined />
</Variant>
</Component>
<Component name="Bars">
<Variant>
<BarsOutlined />
</Variant>
</Component>
<Component name="Bell">
<Variant>
<BellOutlined />
</Variant>
</Component>
<Component name="Clear">
<Variant>
<ClearOutlined />
</Variant>
</Component>
<Component name="Download">
<Variant>
<DownloadOutlined />
</Variant>
</Component>
<Component name="Upload">
<Variant>
<UploadOutlined />
</Variant>
</Component>
<Component name="Sync">
<Variant>
<SyncOutlined />
</Variant>
</Component>
<Component name="Save">
<Variant>
<SaveOutlined />
</Variant>
</Component>
<Component name="Search">
<Variant>
<SearchOutlined />
</Variant>
</Component>
<Component name="Settings">
<Variant>
<SettingOutlined />
</Variant>
</Component>
<Component name="Paperclip">
<Variant>
<PaperClipOutlined />
</Variant>
</Component>
<Component name="Phone">
<Variant>
<PhoneOutlined />
</Variant>
</Component>
<Component name="Mail">
<Variant>
<MailOutlined />
</Variant>
</Component>
<Component name="Home">
<Variant>
<HomeOutlined />
</Variant>
</Component>
<Component name="Contacts">
<Variant>
<ContactsOutlined />
</Variant>
</Component>
<Component name="User">
<Variant>
<UserOutlined />
</Variant>
<Variant name="Add">
<UserAddOutlined />
</Variant>
<Variant name="Remove">
<UserDeleteOutlined />
</Variant>
</Component>
<Component name="Team">
<Variant>
<TeamOutlined />
</Variant>
</Component>
</Category>
<Category name="Screens">
<Component name="ExampleCustomScreen">
<Variant>
<ExampleCustomScreen />
</Variant>
</Component>
<Component name="CustomEntityFilterTest">
<Variant>
<CustomEntityFilterTest />
</Variant>
</Component>
<Component name="CustomFormControls">
<Variant>
<CustomFormControls />
</Variant>
</Component>
<Component name="CustomDataDisplayComponents">
<Variant>
<CustomDataDisplayComponents />
</Variant>
</Component>
<Component name="CustomAppLayouts">
<Variant>
<CustomAppLayouts />
</Variant>
</Component>
<Component name="CustomControls">
<Variant>
<CustomControls />
</Variant>
</Component>
<Component name="ErrorBoundaryTests">
<Variant>
<ErrorBoundaryTests />
</Variant>
</Component>
<Component name="TestBlankScreen">
<Variant>
<TestBlankScreen />
</Variant>
</Component>
<Component name="CarEditor">
<Variant>
<CarEditor />
</Variant>
</Component>
<Component name="CarBrowserCards">
<Variant>
<CarBrowserCards />
</Variant>
</Component>
<Component name="CarBrowserList">
<Variant>
<CarBrowserList />
</Variant>
</Component>
<Component name="CarBrowserTable">
<Variant>
<CarBrowserTable />
</Variant>
</Component>
<Component name="CarCardsGrid">
<Variant>
<CarCardsGrid />
</Variant>
</Component>
<Component name="FavoriteCars">
<Variant>
<FavoriteCars />
</Variant>
</Component>
<Component name="CarCardsWithDetails">
<Variant>
<CarCardsWithDetails />
</Variant>
</Component>
<Component name="CarTableWithFilters">
<Variant>
<CarTableWithFilters />
</Variant>
</Component>
<Component name="CarMasterDetail">
<Variant>
<CarMasterDetail />
</Variant>
</Component>
<Component name="FormWizardCompositionO2O">
<Variant>
<FormWizardCompositionO2O />
</Variant>
</Component>
<Component name="FormWizardEditor">
<Variant>
<FormWizardEditor />
</Variant>
</Component>
<Component name="FormWizardBrowserTable">
<Variant>
<FormWizardBrowserTable />
</Variant>
</Component>
<Component name="CarMultiSelectionTable">
<Variant>
<CarMultiSelectionTable />
</Variant>
</Component>
<Component name="DatatypesTestEditor">
<Variant>
<DatatypesTestEditor />
</Variant>
</Component>
<Component name="DatatypesTestBrowserCards">
<Variant>
<DatatypesTestBrowserCards />
</Variant>
</Component>
<Component name="DatatypesTestBrowserList">
<Variant>
<DatatypesTestBrowserList />
</Variant>
</Component>
<Component name="DatatypesTestBrowserTable">
<Variant>
<DatatypesTestBrowserTable />
</Variant>
</Component>
<Component name="DatatypesTestCards">
<Variant>
<DatatypesTestCards />
</Variant>
</Component>
<Component name="AssociationO2OEditor">
<Variant>
<AssociationO2OEditor />
</Variant>
</Component>
<Component name="AssociationO2OBrowserTable">
<Variant>
<AssociationO2OBrowserTable />
</Variant>
</Component>
<Component name="AssociationO2MEditor">
<Variant>
<AssociationO2MEditor />
</Variant>
</Component>
<Component name="AssociationO2MBrowserTable">
<Variant>
<AssociationO2MBrowserTable />
</Variant>
</Component>
<Component name="AssociationM2OEditor">
<Variant>
<AssociationM2OEditor />
</Variant>
</Component>
<Component name="AssociationM2OBrowserTable">
<Variant>
<AssociationM2OBrowserTable />
</Variant>
</Component>
<Component name="AssociationM2MEditor">
<Variant>
<AssociationM2MEditor />
</Variant>
</Component>
<Component name="AssociationM2MBrowserTable">
<Variant>
<AssociationM2MBrowserTable />
</Variant>
</Component>
<Component name="CompositionO2OEditor">
<Variant>
<CompositionO2OEditor />
</Variant>
</Component>
<Component name="CompositionO2OBrowserTable">
<Variant>
<CompositionO2OBrowserTable />
</Variant>
</Component>
<Component name="CompositionO2MEditor">
<Variant>
<CompositionO2MEditor />
</Variant>
</Component>
<Component name="CompositionO2MBrowserTable">
<Variant>
<CompositionO2MBrowserTable />
</Variant>
</Component>
<Component name="DeeplyNestedTestEntityEditor">
<Variant>
<DeeplyNestedTestEntityEditor />
</Variant>
</Component>
<Component name="DeeplyNestedO2MTestEntityTable">
<Variant>
<DeeplyNestedO2MTestEntityTable />
</Variant>
</Component>
<Component name="DeeplyNestedO2MTestEntityEditor">
<Variant>
<DeeplyNestedO2MTestEntityEditor />
</Variant>
</Component>
<Component name="IntIdEditor">
<Variant>
<IntIdEditor />
</Variant>
</Component>
<Component name="IntIdBrowserTable">
<Variant>
<IntIdBrowserTable />
</Variant>
</Component>
<Component name="IntIdBrowserCards">
<Variant>
<IntIdBrowserCards />
</Variant>
</Component>
<Component name="IntIdBrowserList">
<Variant>
<IntIdBrowserList />
</Variant>
</Component>
<Component name="IntIdentityIdCards">
<Variant>
<IntIdentityIdCards />
</Variant>
</Component>
<Component name="IntIdentityIdEditor">
<Variant>
<IntIdentityIdEditor />
</Variant>
</Component>
<Component name="IntIdentityIdBrowserTable">
<Variant>
<IntIdentityIdBrowserTable />
</Variant>
</Component>
<Component name="IntIdentityIdBrowserCards">
<Variant>
<IntIdentityIdBrowserCards />
</Variant>
</Component>
<Component name="IntIdentityIdBrowserList">
<Variant>
<IntIdentityIdBrowserList />
</Variant>
</Component>
<Component name="StringIdCards">
<Variant>
<StringIdCards />
</Variant>
</Component>
<Component name="StringIdMgtCardsEdit">
<Variant>
<StringIdMgtCardsEdit />
</Variant>
</Component>
<Component name="StringIdBrowserCards">
<Variant>
<StringIdBrowserCards />
</Variant>
</Component>
<Component name="StringIdBrowserList">
<Variant>
<StringIdBrowserList />
</Variant>
</Component>
<Component name="StringIdBrowserTable">
<Variant>
<StringIdBrowserTable />
</Variant>
</Component>
<Component name="WeirdStringIdEditor">
<Variant>
<WeirdStringIdEditor />
</Variant>
</Component>
<Component name="WeirdStringIdBrowserCards">
<Variant>
<WeirdStringIdBrowserCards />
</Variant>
</Component>
<Component name="WeirdStringIdBrowserList">
<Variant>
<WeirdStringIdBrowserList />
</Variant>
</Component>
<Component name="WeirdStringIdBrowserTable">
<Variant>
<WeirdStringIdBrowserTable />
</Variant>
</Component>
<Component name="BoringStringIdEditor">
<Variant>
<BoringStringIdEditor />
</Variant>
</Component>
<Component name="BoringStringIdBrowserTable">
<Variant>
<BoringStringIdBrowserTable />
</Variant>
</Component>
<Component name="TrickyIdEditor">
<Variant>
<TrickyIdEditor />
</Variant>
</Component>
<Component name="TrickyIdBrowserTable">
<Variant>
<TrickyIdBrowserTable />
</Variant>
</Component>
</Category>
</Palette>
)
Example #18
Source File: index.tsx From Aragorn with MIT License | 4 votes |
FileManage = () => {
const {
state: {
uploaderProfiles,
configuration: { defaultUploaderProfileId }
}
} = useAppContext();
const [windowHeight, setWindowHeight] = useState(window.innerHeight);
useEffect(() => {
function handleResize() {
setWindowHeight(window.innerHeight);
}
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
const { id } = useParams<{ id: string }>();
const [hasFileManageFeature, setHasFileManageFeature] = useState(false);
const [uploaderProfile, setUploaderProfile] = useState({} as UploaderProfile);
useEffect(() => {
const currentId = id || defaultUploaderProfileId;
setCurrentProfile(currentId as string);
}, []);
useEffect(() => {
if (uploaderProfile?.id) {
getList();
}
}, [uploaderProfile]);
const [list, setList] = useState([] as ListFile[]);
const [listLoading, setListLoading] = useState(false);
const getList = (directoryPath?: string) => {
setListLoading(true);
ipcRenderer.send('file-list-get', uploaderProfile.id, directoryPath);
};
const [dirPath, setDirPath] = useState([] as string[]);
useEffect(() => {
function handleListGetReply(_, res?: FileListResponse) {
setListLoading(false);
if (res === undefined) {
setHasFileManageFeature(false);
setList([]);
message.info(`${uploaderProfile.uploaderName}暂不支持文件管理功能`);
return;
}
setHasFileManageFeature(true);
if (res.success) {
setList(res.data);
} else {
message.error(`文件列表获取失败 ${res.desc || ''}`);
}
}
function handleFileDeleteReply(_, res?: DeleteFileResponse) {
if (res === undefined) {
return;
}
if (res.success) {
message.success({ content: '文件删除成功', key: 'file-manage-delete' });
getList(dirPath.join('/'));
} else {
message.error({ content: `文件删除失败 ${res.desc || ''}`, key: 'file-manage-delete' });
}
}
function handleFileUploadReply() {
getList(dirPath.join('/'));
}
function handleDirectoryCreateReply(_, res?: CreateDirectoryResponse) {
if (res === undefined) {
return;
}
if (res.success) {
message.success('目录创建成功');
setModalVisible(false);
getList(dirPath.join('/'));
} else {
message.error(`目录创建失败 ${res.desc || ''}`);
}
}
function handleExportReplay(_, res) {
setExportLoading(false);
if (res) {
shell.showItemInFolder(res);
setRowKeys([]);
setSelectRows([]);
}
}
ipcRenderer.on('file-list-get-reply', handleListGetReply);
ipcRenderer.on('file-delete-reply', handleFileDeleteReply);
ipcRenderer.on('file-upload-reply', handleFileUploadReply);
ipcRenderer.on('directory-create-reply', handleDirectoryCreateReply);
ipcRenderer.on('export-reply', handleExportReplay);
return () => {
ipcRenderer.removeListener('file-list-get-reply', handleListGetReply);
ipcRenderer.removeListener('file-delete-reply', handleFileDeleteReply);
ipcRenderer.removeListener('file-upload-reply', handleFileUploadReply);
ipcRenderer.removeListener('directory-create-reply', handleDirectoryCreateReply);
ipcRenderer.removeListener('export-reply', handleExportReplay);
};
}, [uploaderProfile, dirPath]);
const handleNameClick = (record: ListFile) => {
if (record.type === 'directory') {
const newPath = [...dirPath, formatFileName(record.name)];
setDirPath(newPath);
getList(newPath.join('/'));
} else {
clipboard.writeText(record.url as string);
message.success('链接已复制到粘贴板');
}
};
const handlePathClick = (index: number) => {
if (index === -1) {
setDirPath([]);
getList();
} else {
const newPath = dirPath.slice(0, index + 1);
setDirPath(newPath);
getList(newPath.join('/'));
}
};
const setCurrentProfile = (uploaderProfileId: string) => {
setDirPath([]);
const uploaderProfile = uploaderProfiles.find(item => item.id === uploaderProfileId);
setUploaderProfile(uploaderProfile as UploaderProfile);
};
const formatFileName = (name: string) => {
if (dirPath.length > 0) {
const pathPrefix = dirPath.join('/') + '/';
return name.split(pathPrefix).pop() || '';
} else {
return name;
}
};
const [selectRowKeys, setRowKeys] = useState([] as string[]);
const [selectRows, setSelectRows] = useState([] as ListFile[]);
const handleTableRowChange = (selectedRowKeys, selectedRows: ListFile[]) => {
setRowKeys(selectedRowKeys);
setSelectRows(selectedRows);
};
const handleRefresh = () => {
getList(dirPath.join('/'));
};
const handleBatchDelete = () => {
Modal.confirm({
title: '确认删除',
onOk: () => {
const names = selectRows.map(item => [...dirPath, formatFileName(item.name)].join('/'));
message.info({ content: '正在删除,请稍后...', key: 'file-manage-delete' });
ipcRenderer.send('file-delete', uploaderProfile.id, names);
}
});
};
const handleDelete = (record: ListFile) => {
let name = record.name;
Modal.confirm({
title: '确认删除',
content: name,
onOk: () => {
let name = record.name;
if (record.type === 'directory') {
name = `${[...dirPath, record.name].join('/')}/`;
} else {
name = [...dirPath, formatFileName(record.name)].join('/');
}
message.info({ content: '正在删除,请稍后...', key: 'file-manage-delete' });
ipcRenderer.send('file-delete', uploaderProfile.id, [name]);
}
});
};
const uploadRef = useRef<HTMLInputElement>(null);
const handleFileUpload = (event: React.FormEvent<HTMLInputElement>) => {
const fileList = event.currentTarget.files || [];
const filesPath = Array.from(fileList).map(file => file.path);
const pathPrefix = dirPath.join('/');
ipcRenderer.send('file-upload', uploaderProfile.id, filesPath, pathPrefix);
event.currentTarget.value = '';
};
const [modalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
const handleCreateDirectory = () => {
form.validateFields().then(values => {
ipcRenderer.send('directory-create', uploaderProfile.id, values?.directoryPath || '');
});
};
const handleDownload = (record: ListFile) => {
ipcRenderer.send('file-download', record.name, record.url);
};
const [exportLoading, setExportLoading] = useState(false);
const handleExport = () => {
const data = selectRows.map(item => {
const fileNameArr = item.name.split('.');
fileNameArr.pop();
return {
name: fileNameArr.join('.'),
url: item.url
};
});
setExportLoading(true);
ipcRenderer.send('export', data);
};
const columns: ColumnsType<ListFile> = [
{
title: '文件名',
dataIndex: 'name',
ellipsis: true,
render: (val: string, record: ListFile) => (
<div style={{ display: 'flex', alignItems: 'center' }}>
{record.type === 'directory' ? (
<FolderFilled style={{ fontSize: 16 }} />
) : (
<FileOutlined style={{ fontSize: 16 }} />
)}
{record.type === 'directory' ? (
<a
title={val}
onClick={() => handleNameClick(record)}
className="table-filename"
style={{ marginLeft: 10, overflow: 'hidden', textOverflow: 'ellipsis' }}
>
{formatFileName(val)}
</a>
) : (
<Popover
placement="topLeft"
content={() =>
/(jpg|png|gif|jpeg)$/.test(val) ? (
<Image
style={{ maxWidth: 500 }}
src={record.url}
fallback="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="
/>
) : (
val
)
}
trigger="hover"
>
<a
title={val}
onClick={() => handleNameClick(record)}
className="table-filename"
style={{ marginLeft: 10, overflow: 'hidden', textOverflow: 'ellipsis' }}
>
{formatFileName(val)}
</a>
</Popover>
)}
</div>
)
},
{
title: '文件大小',
dataIndex: 'size',
ellipsis: true,
width: 120,
render: val => (val ? filesize(val) : '-')
},
{
title: '更新时间',
dataIndex: 'lastModified',
ellipsis: true,
width: 200,
render: val => (val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : '-')
},
{
title: '操作',
width: 120,
render: (_, record) => (
<Space>
{record.type !== 'directory' && (
<>
<DownloadOutlined onClick={() => handleDownload(record)} />
<CopyOutlined onClick={() => handleNameClick(record)} />
</>
)}
<DeleteOutlined onClick={() => handleDelete(record)} />
</Space>
)
}
];
return (
<div className="storage-page">
<header>
<span>文件管理</span>
<Divider />
</header>
<Space style={{ marginBottom: 10 }}>
<Select style={{ minWidth: 120 }} value={uploaderProfile?.id} onChange={setCurrentProfile}>
{uploaderProfiles.map(item => (
<Select.Option key={item.name} value={item.id}>
{item.name}
</Select.Option>
))}
</Select>
<Button
title="上传"
icon={<UploadOutlined />}
disabled={!hasFileManageFeature}
type="primary"
onClick={() => {
uploadRef.current?.click();
}}
/>
<Button title="刷新" icon={<ReloadOutlined />} disabled={!hasFileManageFeature} onClick={handleRefresh} />
<Button
title="创建文件夹"
icon={<FolderAddOutlined />}
disabled={!hasFileManageFeature}
onClick={() => {
setModalVisible(true);
}}
/>
<Button
title="导出"
icon={<ExportOutlined />}
disabled={selectRows.length === 0}
onClick={handleExport}
loading={exportLoading}
/>
<Button title="删除" icon={<DeleteOutlined />} disabled={selectRows.length === 0} onClick={handleBatchDelete} />
</Space>
<Breadcrumb style={{ marginBottom: 10 }}>
<Breadcrumb.Item>
<a onClick={() => handlePathClick(-1)}>全部文件</a>
</Breadcrumb.Item>
{dirPath.map((item, index) => (
<Breadcrumb.Item key={item}>
<a onClick={() => handlePathClick(index)}>{item}</a>
</Breadcrumb.Item>
))}
</Breadcrumb>
<div className="table-wrapper">
<Table
size="small"
rowKey="name"
scroll={{ y: windowHeight - 270 }}
dataSource={list}
columns={columns}
pagination={{
size: 'small',
defaultPageSize: 100,
pageSizeOptions: ['50', '100', '200'],
hideOnSinglePage: true
}}
loading={listLoading}
rowSelection={{
onChange: handleTableRowChange,
selectedRowKeys: selectRowKeys,
getCheckboxProps: record => ({ disabled: record?.type === 'directory' })
}}
/>
</div>
<input ref={uploadRef} type="file" multiple hidden onChange={handleFileUpload} />
<Modal
title="创建目录"
visible={modalVisible}
onCancel={() => setModalVisible(false)}
onOk={handleCreateDirectory}
destroyOnClose={true}
>
<Form form={form} preserve={false}>
<Form.Item
label="目录名称"
name="directoryPath"
rules={[{ required: true }, { pattern: domainPathRegExp, message: '目录名不能以 / 开头或结尾' }]}
>
<Input autoFocus />
</Form.Item>
</Form>
</Modal>
</div>
);
}
Example #19
Source File: index.tsx From fe-v5 with Apache License 2.0 | 4 votes |
Indicator: React.FC = () => {
const { t } = useTranslation();
// 数据定义
const [form] = Form.useForm();
const tableRef = useRef(null as any);
const exportTextRef = useRef(null as any);
const [modal, setModal] = useState<boolean>(false);
const [modalType, setModalType] = useState('create');
const [exportList, setExportList] = useState([] as string[]);
const [editingKey, setEditingKey] = useState<Partial<InterfaceItem>>({});
const [query, setQuery] = useState<string>('');
const history = useHistory();
const moreOperations: MoreOptions = {
导入指标: 'import',
导出指标: 'export',
};
const optMap = {
import: t('导入'),
edit: t('编辑'),
export: t('导出'),
create: t('创建'),
}; // 弹窗操作
const handleOpen = (type: string = 'create') => {
setModalType(type);
setModal(true);
};
const handleCancel = () => {
setModal(false);
}; // 接口操作
const handleEdit = (item: InterfaceItem) => {
setEditingKey(item);
};
const handleDelete = (item) => {
deleteIndicator([item.id]).then(async (res) => {
await tableRef.current.refreshList();
message.success(t('删除指标成功'));
});
};
const handleAdd = async () => {
try {
const values = await form.validateFields();
addIndicator(values.metrics || '').then(async (res) => {
if (res && res.success) {
await tableRef.current.refreshList();
setModal(false);
message.success(t('创建指标成功'));
}
});
} catch (e) {}
}; // 更多操作
const [oType, setOType] = useState<string>(t('更多操作'));
const onSelect = (val: string): void => {
switch (val) {
case 'import':
setModal(true);
setModalType('import');
break;
case 'export':
if (exportList.length <= 0) {
message.warning(t('请勾选需要导出的指标'));
} else {
setModal(true);
setModalType('export');
setTimeout(() => {
exportTextRef.current && exportTextRef.current.focus();
});
}
break;
default:
break;
}
setOType(val);
}; // table
const handleEditChange = (e, record, index, field) => {
let targetCol = Object.assign({}, record);
targetCol[field] = e.target.value;
setEditingKey(targetCol);
};
const renderInput = (text, record, index, field) => {
if (record.id === editingKey.id) {
return (
<Input
defaultValue={text}
onPressEnter={() => handleEditOption('save')}
onChange={(e) => handleEditChange(e, record, index, field)}
/>
);
} else {
return <span>{text}</span>;
}
};
const handleEditOption = async (type = '') => {
if (type === 'save') {
let id = editingKey.id || -1;
await editIndicator(id, {
description: editingKey.description,
metric: editingKey.metric,
});
message.success(t('编辑成功'));
tableRef.current.refreshList();
}
setEditingKey({} as any);
};
const columns = [
{
title: t('指标名称'),
dataIndex: 'metric',
key: 'metric',
render: (text: string, record: InterfaceItem) => {
return (
<a
onClick={() => {
let path = {
pathname: `/metric/explorer`,
state: { name: text, description: record.description },
};
history.push(path);
}}
>
{text}
</a>
);
},
},
{
title: t('释义'),
dataIndex: 'description',
width: '40%',
key: 'description',
render: (text: string, record: InterfaceItem, index: number) => {
return renderInput(text, record, index, 'description');
},
},
{
title: t('操作'),
dataIndex: '',
key: 'operations',
width: '15%',
render: (_: any, item: any) => {
return (
<div className='operations'>
{item.id !== editingKey.id ? (
<div>
<span
className='operation-item edit'
onClick={() => handleEdit(item)}
>
{t('编辑')}
</span>
{/* <Popconfirm
title='确定删除该指标?'
onConfirm={() => handleDelete(item)}
okText='确定'
cancelText='取消'
>
<span className='operation-item delete'>删除</span>
</Popconfirm> */}
<div
className='table-operator-area-warning'
style={{
display: 'inline-block',
marginLeft: 10,
}}
onClick={() => {
confirm({
title: t('确定删除该指标?'),
icon: <ExclamationCircleOutlined />,
onOk: () => {
handleDelete(item);
},
});
}}
>
{t('删除')}
</div>
</div>
) : (
<div>
<span
className='operation-item edit-save'
onClick={() => handleEditOption('save')}
>
{t('保存')}
</span>
<span
className='operation-item edit-cancel'
onClick={() => handleEditOption('cancel')}
>
{t('取消')}
</span>
</div>
)}
</div>
);
},
},
];
const onSearchQuery = (e) => {
let val = e.target.value;
setQuery(val);
};
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
let list: string[] = [];
selectedRows.map((item) => {
list.push(`${item.metric}:${item.description}`);
});
setExportList(list);
},
};
const handleExportTxt = () => {
download(exportList);
};
return (
<PageLayout title={t('指标释义')} icon={<BulbOutlined />}>
<div className='indicator-index'>
<div className='indicator-operations'>
<Input
className={'searchInput'}
onPressEnter={onSearchQuery}
prefix={<SearchOutlined />}
placeholder={t('指标名称或释义')}
/>
<div className='operations'>
<Button.Group>
<Button
size='middle'
type='default'
icon={<DownloadOutlined />}
onClick={() => onSelect('import')}
>
{t('导入')}
</Button>
<Button
size='middle'
type='default'
icon={<UploadOutlined />}
onClick={() => onSelect('export')}
>
{t('导出')}
</Button>
</Button.Group>
</div>
</div>
<div className='indicator-main'>
<BaseTable
fetchHandle={getIndicatorList}
ref={tableRef}
fetchParams={{
query: query,
}}
rowSelection={{ ...rowSelection }}
rowKey={(record: { id: number }) => {
return record.id;
}}
columns={columns}
/>
</div>
</div>
<Modal
title={optMap[modalType]}
destroyOnClose={true}
footer={
modalType !== 'export' && [
<Button key='delete' onClick={handleCancel}>
{t('取消')}
</Button>,
<Button key='submit' type='primary' onClick={handleAdd}>
{t('确定')}
</Button>,
]
}
onCancel={handleCancel}
visible={modal}
>
<p
style={{
color: '#999',
}}
>
{modalType === 'export' ? (
<a onClick={handleExportTxt}>Download.txt</a>
) : (
<span>{t('一行一个')}</span>
)}
</p>
{(() => {
switch (modalType) {
case 'export':
return (
<div
contentEditable='true'
suppressContentEditableWarning={true}
ref={exportTextRef}
style={{
height: '200px',
border: '1px solid #d9d9d9',
overflow: 'auto',
padding: '10px',
}}
>
{exportList.map((item, index) => {
return <div key={index}>{item}</div>;
})}
</div>
);
break;
case 'import':
case 'create':
return (
<Form name={modalType + '-dialog'} form={form} preserve={false}>
<Form.Item
name={'metrics'}
key={'metrics'}
rules={[
{
required: true,
message: t('不能为空'),
validateTrigger: 'trigger',
},
]}
>
<TextArea
placeholder={'name:description'}
rows={4}
></TextArea>
</Form.Item>
</Form>
);
break;
}
})()}
</Modal>
</PageLayout>
);
}
Example #20
Source File: CodeDebugger.tsx From jitsu with MIT License | 4 votes |
ControlsComponent: React.FC<ControlsProps> = ({
inputChecked,
codeChecked,
outputChecked,
codeSaved,
toggleInput,
toggleCode,
toggleOutput,
handleExit: handleCloseWithoutSaving,
handleSave,
handleRun,
}) => {
const [isClosePopoverVisible, setIsClosePopoverVisible] = useState(false)
const handleClose = () => {
if (!codeSaved.current) {
setIsClosePopoverVisible(true)
return
}
handleCloseWithoutSaving()
}
useEffect(() => {
const handleToggleInput = () => {
toggleInput()
return false // to prevent browsers' default behaviour
}
const handleToggleCode = () => {
toggleCode()
return false
}
const handleToggleOutput = () => {
toggleOutput()
return false
}
const _handleSave = (e: KeyboardEvent) => {
e.preventDefault()
handleSave()
return false
}
const _handleRun = (e: KeyboardEvent) => {
e.stopPropagation()
handleRun()
return false
}
const handleEscape = e => {
if (e.key === "Escape") {
handleClose()
}
}
hotkeys.filter = () => true // to enable hotkeys everywhere, even in input fields
hotkeys("cmd+i,ctrl+i", handleToggleInput)
hotkeys("cmd+u,ctrl+u", handleToggleCode)
hotkeys("cmd+o,ctrl+o", handleToggleOutput)
hotkeys("cmd+s,ctrl+s", _handleSave)
hotkeys("cmd+enter,ctrl+enter", _handleRun)
document.addEventListener("keydown", handleEscape, true)
return () => {
hotkeys.unbind("cmd+i,ctrl+i", handleToggleInput)
hotkeys.unbind("cmd+u,ctrl+u", handleToggleCode)
hotkeys.unbind("cmd+o,ctrl+o", handleToggleOutput)
hotkeys.unbind("cmd+s,ctrl+s", _handleSave)
hotkeys.unbind("cmd+enter,ctrl+enter", _handleRun)
document.removeEventListener("keydown", handleEscape, true)
}
}, [])
return (
<div className="flex items-stretch w-full h-full">
<Popconfirm
title="You have some unsaved expression code. Do you want to quit?"
placement="rightBottom"
className="max-w-xs mr-4"
visible={isClosePopoverVisible}
onCancel={() => setIsClosePopoverVisible(false)}
onConfirm={() => {
handleCloseWithoutSaving()
setIsClosePopoverVisible(false)
}}
>
<Button size="middle" className="flex-grow-0" onClick={handleClose}>
<CloseOutlined className={styles.adaptiveIcon} />
<span className={`${styles.adaptiveLabel} ${styles.noMargins}`}>{"Close"}</span>
</Button>
</Popconfirm>
<div className="flex justify-center items-center flex-auto min-w-0">
<Tooltip title={`${OS_CMD_CTRL_KEY}+I`} mouseEnterDelay={1}>
<Checkbox
checked={inputChecked}
className={cn("relative", styles.checkbox, styles.hideAntdCheckbox, styles.checkboxLabel, {
[styles.checkboxChecked]: inputChecked,
})}
onClick={toggleInput}
>
<i className="block absolute left-0.5">{inputChecked ? <EyeFilled /> : <EyeInvisibleFilled />}</i>
<span className={styles.adaptiveIcon}>{"{ }"}</span>
<span className={`${styles.adaptiveLabel} ${styles.noMargins}`}>{"Input"}</span>
</Checkbox>
</Tooltip>
<Tooltip title={`${OS_CMD_CTRL_KEY}+U`} mouseEnterDelay={1}>
<Checkbox
checked={codeChecked}
className={cn("relative", styles.checkbox, styles.hideAntdCheckbox, styles.checkboxLabel, {
[styles.checkboxChecked]: codeChecked,
})}
onClick={toggleCode}
>
<i className="block absolute left-0.5">{codeChecked ? <EyeFilled /> : <EyeInvisibleFilled />}</i>
<span className={styles.adaptiveIcon}>{"</>"}</span>
<span className={`${styles.adaptiveLabel} ${styles.noMargins}`}>{"Expression"}</span>
</Checkbox>
</Tooltip>
<Tooltip title={`${OS_CMD_CTRL_KEY}+O`} mouseEnterDelay={1}>
<Checkbox
checked={outputChecked}
className={cn("relative", styles.checkbox, styles.hideAntdCheckbox, styles.checkboxLabel, {
[styles.checkboxChecked]: outputChecked,
})}
onClick={toggleOutput}
>
<i className="block absolute left-0.5">{outputChecked ? <EyeFilled /> : <EyeInvisibleFilled />}</i>
<CodeOutlined className={styles.adaptiveIcon} />
<span className={`${styles.adaptiveLabel} ${styles.noMargins}`}>{"Result"}</span>
</Checkbox>
</Tooltip>
</div>
<div className="flex-grow-0 ant-btn-group">
<Tooltip title={`${OS_CMD_CTRL_KEY}+↵`} mouseEnterDelay={1}>
<Button
size="middle"
type="primary"
icon={<CaretRightOutlined />}
className={`${styles.buttonGreen}`}
onClick={handleRun}
>
<span className={`${styles.adaptiveLabel}`}>{"Run"}</span>
</Button>
</Tooltip>
<Tooltip title={`${OS_CMD_CTRL_KEY}+S`} mouseEnterDelay={1}>
<Button size="middle" type="primary" onClick={handleSave} icon={<DownloadOutlined />}>
<span className={`${styles.adaptiveLabel}`}>{"Save"}</span>
</Button>
</Tooltip>
</div>
</div>
)
}
Example #21
Source File: Icon.tsx From html2sketch with MIT License | 4 votes |
IconSymbol: FC = () => {
return (
<Row>
{/*<CaretUpOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/1.CaretUpOutlined'}*/}
{/*/>*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.MailOutlined'}*/}
{/*/>*/}
{/*<StepBackwardOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
{/*/>*/}
{/*<StepForwardOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
{/*/>*/}
<StepForwardOutlined />
<ShrinkOutlined />
<ArrowsAltOutlined />
<DownOutlined />
<UpOutlined />
<LeftOutlined />
<RightOutlined />
<CaretUpOutlined />
<CaretDownOutlined />
<CaretLeftOutlined />
<CaretRightOutlined />
<VerticalAlignTopOutlined />
<RollbackOutlined />
<FastBackwardOutlined />
<FastForwardOutlined />
<DoubleRightOutlined />
<DoubleLeftOutlined />
<VerticalLeftOutlined />
<VerticalRightOutlined />
<VerticalAlignMiddleOutlined />
<VerticalAlignBottomOutlined />
<ForwardOutlined />
<BackwardOutlined />
<EnterOutlined />
<RetweetOutlined />
<SwapOutlined />
<SwapLeftOutlined />
<SwapRightOutlined />
<ArrowUpOutlined />
<ArrowDownOutlined />
<ArrowLeftOutlined />
<ArrowRightOutlined />
<LoginOutlined />
<LogoutOutlined />
<MenuFoldOutlined />
<MenuUnfoldOutlined />
<BorderBottomOutlined />
<BorderHorizontalOutlined />
<BorderInnerOutlined />
<BorderOuterOutlined />
<BorderLeftOutlined />
<BorderRightOutlined />
<BorderTopOutlined />
<BorderVerticleOutlined />
<PicCenterOutlined />
<PicLeftOutlined />
<PicRightOutlined />
<RadiusBottomleftOutlined />
<RadiusBottomrightOutlined />
<RadiusUpleftOutlined />
<RadiusUprightOutlined />
<FullscreenOutlined />
<FullscreenExitOutlined />
<QuestionOutlined />
<PauseOutlined />
<MinusOutlined />
<PauseCircleOutlined />
<InfoOutlined />
<CloseOutlined />
<ExclamationOutlined />
<CheckOutlined />
<WarningOutlined />
<IssuesCloseOutlined />
<StopOutlined />
<EditOutlined />
<CopyOutlined />
<ScissorOutlined />
<DeleteOutlined />
<SnippetsOutlined />
<DiffOutlined />
<HighlightOutlined />
<AlignCenterOutlined />
<AlignLeftOutlined />
<AlignRightOutlined />
<BgColorsOutlined />
<BoldOutlined />
<ItalicOutlined />
<UnderlineOutlined />
<StrikethroughOutlined />
<RedoOutlined />
<UndoOutlined />
<ZoomInOutlined />
<ZoomOutOutlined />
<FontColorsOutlined />
<FontSizeOutlined />
<LineHeightOutlined />
<SortAscendingOutlined />
<SortDescendingOutlined />
<DragOutlined />
<OrderedListOutlined />
<UnorderedListOutlined />
<RadiusSettingOutlined />
<ColumnWidthOutlined />
<ColumnHeightOutlined />
<AreaChartOutlined />
<PieChartOutlined />
<BarChartOutlined />
<DotChartOutlined />
<LineChartOutlined />
<RadarChartOutlined />
<HeatMapOutlined />
<FallOutlined />
<RiseOutlined />
<StockOutlined />
<BoxPlotOutlined />
<FundOutlined />
<SlidersOutlined />
<AndroidOutlined />
<AppleOutlined />
<WindowsOutlined />
<IeOutlined />
<ChromeOutlined />
<GithubOutlined />
<AliwangwangOutlined />
<DingdingOutlined />
<WeiboSquareOutlined />
<WeiboCircleOutlined />
<TaobaoCircleOutlined />
<Html5Outlined />
<WeiboOutlined />
<TwitterOutlined />
<WechatOutlined />
<AlipayCircleOutlined />
<TaobaoOutlined />
<SkypeOutlined />
<FacebookOutlined />
<CodepenOutlined />
<CodeSandboxOutlined />
<AmazonOutlined />
<GoogleOutlined />
<AlipayOutlined />
<AntDesignOutlined />
<AntCloudOutlined />
<ZhihuOutlined />
<SlackOutlined />
<SlackSquareOutlined />
<BehanceSquareOutlined />
<DribbbleOutlined />
<DribbbleSquareOutlined />
<InstagramOutlined />
<YuqueOutlined />
<AlibabaOutlined />
<YahooOutlined />
<RedditOutlined />
<SketchOutlined />
<AccountBookOutlined />
<AlertOutlined />
<ApartmentOutlined />
<ApiOutlined />
<QqOutlined />
<MediumWorkmarkOutlined />
<GitlabOutlined />
<MediumOutlined />
<GooglePlusOutlined />
<AppstoreAddOutlined />
<AppstoreOutlined />
<AudioOutlined />
<AudioMutedOutlined />
<AuditOutlined />
<BankOutlined />
<BarcodeOutlined />
<BarsOutlined />
<BellOutlined />
<BlockOutlined />
<BookOutlined />
<BorderOutlined />
<BranchesOutlined />
<BuildOutlined />
<BulbOutlined />
<CalculatorOutlined />
<CalendarOutlined />
<CameraOutlined />
<CarOutlined />
<CarryOutOutlined />
<CiCircleOutlined />
<CiOutlined />
<CloudOutlined />
<ClearOutlined />
<ClusterOutlined />
<CodeOutlined />
<CoffeeOutlined />
<CompassOutlined />
<CompressOutlined />
<ContactsOutlined />
<ContainerOutlined />
<ControlOutlined />
<CopyrightCircleOutlined />
<CopyrightOutlined />
<CreditCardOutlined />
<CrownOutlined />
<CustomerServiceOutlined />
<DashboardOutlined />
<DatabaseOutlined />
<DeleteColumnOutlined />
<DeleteRowOutlined />
<DisconnectOutlined />
<DislikeOutlined />
<DollarCircleOutlined />
<DollarOutlined />
<DownloadOutlined />
<EllipsisOutlined />
<EnvironmentOutlined />
<EuroCircleOutlined />
<EuroOutlined />
<ExceptionOutlined />
<ExpandAltOutlined />
<ExpandOutlined />
<ExperimentOutlined />
<ExportOutlined />
<EyeOutlined />
<FieldBinaryOutlined />
<FieldNumberOutlined />
<FieldStringOutlined />
<DesktopOutlined />
<DingtalkOutlined />
<FileAddOutlined />
<FileDoneOutlined />
<FileExcelOutlined />
<FileExclamationOutlined />
<FileOutlined />
<FileImageOutlined />
<FileJpgOutlined />
<FileMarkdownOutlined />
<FilePdfOutlined />
<FilePptOutlined />
<FileProtectOutlined />
<FileSearchOutlined />
<FileSyncOutlined />
<FileTextOutlined />
<FileUnknownOutlined />
<FileWordOutlined />
<FilterOutlined />
<FireOutlined />
<FlagOutlined />
<FolderAddOutlined />
<FolderOutlined />
<FolderOpenOutlined />
<ForkOutlined />
<FormatPainterOutlined />
<FrownOutlined />
<FunctionOutlined />
<FunnelPlotOutlined />
<GatewayOutlined />
<GifOutlined />
<GiftOutlined />
<GlobalOutlined />
<GoldOutlined />
<GroupOutlined />
<HddOutlined />
<HeartOutlined />
<HistoryOutlined />
<HomeOutlined />
<HourglassOutlined />
<IdcardOutlined />
<ImportOutlined />
<InboxOutlined />
<InsertRowAboveOutlined />
<InsertRowBelowOutlined />
<InsertRowLeftOutlined />
<InsertRowRightOutlined />
<InsuranceOutlined />
<InteractionOutlined />
<KeyOutlined />
<LaptopOutlined />
<LayoutOutlined />
<LikeOutlined />
<LineOutlined />
<LinkOutlined />
<Loading3QuartersOutlined />
<LoadingOutlined />
<LockOutlined />
<MailOutlined />
<ManOutlined />
<MedicineBoxOutlined />
<MehOutlined />
<MenuOutlined />
<MergeCellsOutlined />
<MessageOutlined />
<MobileOutlined />
<MoneyCollectOutlined />
<MonitorOutlined />
<MoreOutlined />
<NodeCollapseOutlined />
<NodeExpandOutlined />
<NodeIndexOutlined />
<NotificationOutlined />
<NumberOutlined />
<PaperClipOutlined />
<PartitionOutlined />
<PayCircleOutlined />
<PercentageOutlined />
<PhoneOutlined />
<PictureOutlined />
<PoundCircleOutlined />
<PoundOutlined />
<PoweroffOutlined />
<PrinterOutlined />
<ProfileOutlined />
<ProjectOutlined />
<PropertySafetyOutlined />
<PullRequestOutlined />
<PushpinOutlined />
<QrcodeOutlined />
<ReadOutlined />
<ReconciliationOutlined />
<RedEnvelopeOutlined />
<ReloadOutlined />
<RestOutlined />
<RobotOutlined />
<RocketOutlined />
<SafetyCertificateOutlined />
<SafetyOutlined />
<ScanOutlined />
<ScheduleOutlined />
<SearchOutlined />
<SecurityScanOutlined />
<SelectOutlined />
<SendOutlined />
<SettingOutlined />
<ShakeOutlined />
<ShareAltOutlined />
<ShopOutlined />
<ShoppingCartOutlined />
<ShoppingOutlined />
<SisternodeOutlined />
<SkinOutlined />
<SmileOutlined />
<SolutionOutlined />
<SoundOutlined />
<SplitCellsOutlined />
<StarOutlined />
<SubnodeOutlined />
<SyncOutlined />
<TableOutlined />
<TabletOutlined />
<TagOutlined />
<TagsOutlined />
<TeamOutlined />
<ThunderboltOutlined />
<ToTopOutlined />
<ToolOutlined />
<TrademarkCircleOutlined />
<TrademarkOutlined />
<TransactionOutlined />
<TrophyOutlined />
<UngroupOutlined />
<UnlockOutlined />
<UploadOutlined />
<UsbOutlined />
<UserAddOutlined />
<UserDeleteOutlined />
<UserOutlined />
<UserSwitchOutlined />
<UsergroupAddOutlined />
<UsergroupDeleteOutlined />
<VideoCameraOutlined />
<WalletOutlined />
<WifiOutlined />
<BorderlessTableOutlined />
<WomanOutlined />
<BehanceOutlined />
<DropboxOutlined />
<DeploymentUnitOutlined />
<UpCircleOutlined />
<DownCircleOutlined />
<LeftCircleOutlined />
<RightCircleOutlined />
<UpSquareOutlined />
<DownSquareOutlined />
<LeftSquareOutlined />
<RightSquareOutlined />
<PlayCircleOutlined />
<QuestionCircleOutlined />
<PlusCircleOutlined />
<PlusSquareOutlined />
<MinusSquareOutlined />
<MinusCircleOutlined />
<InfoCircleOutlined />
<ExclamationCircleOutlined />
<CloseCircleOutlined />
<CloseSquareOutlined />
<CheckCircleOutlined />
<CheckSquareOutlined />
<ClockCircleOutlined />
<FormOutlined />
<DashOutlined />
<SmallDashOutlined />
<YoutubeOutlined />
<CodepenCircleOutlined />
<AliyunOutlined />
<PlusOutlined />
<LinkedinOutlined />
<AimOutlined />
<BugOutlined />
<CloudDownloadOutlined />
<CloudServerOutlined />
<CloudSyncOutlined />
<CloudUploadOutlined />
<CommentOutlined />
<ConsoleSqlOutlined />
<EyeInvisibleOutlined />
<FileGifOutlined />
<DeliveredProcedureOutlined />
<FieldTimeOutlined />
<FileZipOutlined />
<FolderViewOutlined />
<FundProjectionScreenOutlined />
<FundViewOutlined />
<MacCommandOutlined />
<PlaySquareOutlined />
<OneToOneOutlined />
<RotateLeftOutlined />
<RotateRightOutlined />
<SaveOutlined />
<SwitcherOutlined />
<TranslationOutlined />
<VerifiedOutlined />
<VideoCameraAddOutlined />
<WhatsAppOutlined />
{/*</Col>*/}
</Row>
);
}
Example #22
Source File: PersonsModal.tsx From posthog-foss with MIT License | 4 votes |
export function PersonsModal({
visible,
view,
filters,
onSaveCohort,
showModalActions = true,
aggregationTargetLabel,
}: PersonsModalProps): JSX.Element {
const {
people,
loadingMorePeople,
firstLoadedPeople,
searchTerm,
isInitialLoad,
clickhouseFeaturesEnabled,
peopleParams,
actorLabel,
sessionRecordingId,
} = useValues(personsModalLogic)
const {
hidePeople,
loadMorePeople,
setFirstLoadedActors,
setPersonsModalFilters,
setSearchTerm,
switchToDataPoint,
openRecordingModal,
closeRecordingModal,
} = useActions(personsModalLogic)
const { preflight } = useValues(preflightLogic)
const { featureFlags } = useValues(featureFlagLogic)
const title = useMemo(
() =>
isInitialLoad ? (
`Loading ${aggregationTargetLabel.plural}…`
) : filters.shown_as === 'Stickiness' ? (
<>
<PropertyKeyInfo value={people?.label || ''} disablePopover /> stickiness on day {people?.day}
</>
) : filters.display === 'ActionsBarValue' || filters.display === 'ActionsPie' ? (
<PropertyKeyInfo value={people?.label || ''} disablePopover />
) : filters.insight === InsightType.FUNNELS ? (
<>
{(people?.funnelStep ?? 0) >= 0 ? 'Completed' : 'Dropped off at'} step{' '}
{Math.abs(people?.funnelStep ?? 0)} • <PropertyKeyInfo value={people?.label || ''} disablePopover />{' '}
{!!people?.breakdown_value ? `• ${people.breakdown_value}` : ''}
</>
) : filters.insight === InsightType.PATHS ? (
<>
{people?.pathsDropoff ? 'Dropped off after' : 'Completed'} step{' '}
<PropertyKeyInfo value={people?.label.replace(/(^[0-9]+_)/, '') || ''} disablePopover />
</>
) : (
<>
{capitalizeFirstLetter(actorLabel)} list on{' '}
<DateDisplay interval={filters.interval || 'day'} date={people?.day?.toString() || ''} />
</>
),
[filters, people, isInitialLoad]
)
const flaggedInsights = featureFlags[FEATURE_FLAGS.NEW_INSIGHT_COHORTS]
const isDownloadCsvAvailable: boolean = view === InsightType.TRENDS && showModalActions && !!people?.action
const isSaveAsCohortAvailable =
clickhouseFeaturesEnabled &&
(view === InsightType.TRENDS ||
view === InsightType.STICKINESS ||
(!!flaggedInsights && (view === InsightType.FUNNELS || view === InsightType.PATHS))) && // make sure flaggedInsights isn't evaluated as undefined
showModalActions
const colorList = getChartColors('white', people?.crossDataset?.length)
const showCountedByTag = !!people?.crossDataset?.find(({ action }) => action?.math && action.math !== 'total')
const hasMultipleSeries = !!people?.crossDataset?.find(({ action }) => action?.order)
return (
<>
{!!sessionRecordingId && <SessionPlayerDrawer onClose={closeRecordingModal} />}
<Modal
title={title}
visible={visible}
onCancel={hidePeople}
footer={
people &&
people.count > 0 &&
(isDownloadCsvAvailable || isSaveAsCohortAvailable) && (
<>
{isDownloadCsvAvailable && (
<Button
icon={<DownloadOutlined />}
href={api.actions.determinePeopleCsvUrl(
{
label: people.label,
action: people.action,
date_from: people.day,
date_to: people.day,
breakdown_value: people.breakdown_value,
},
filters
)}
style={{ marginRight: 8 }}
data-attr="person-modal-download-csv"
>
Download CSV
</Button>
)}
{isSaveAsCohortAvailable && (
<Button
onClick={onSaveCohort}
icon={<UsergroupAddOutlined />}
data-attr="person-modal-save-as-cohort"
>
Save as cohort
</Button>
)}
</>
)
}
width={600}
className="person-modal"
>
{isInitialLoad ? (
<div style={{ padding: 16 }}>
<Skeleton active />
</div>
) : (
people && (
<>
{!preflight?.is_clickhouse_enabled && (
<Input.Search
allowClear
enterButton
placeholder="Search for persons by email, name, or ID"
onChange={(e) => {
setSearchTerm(e.target.value)
if (!e.target.value) {
setFirstLoadedActors(firstLoadedPeople)
}
}}
value={searchTerm}
onSearch={(term) =>
term
? setPersonsModalFilters(term, people, filters)
: setFirstLoadedActors(firstLoadedPeople)
}
/>
)}
{featureFlags[FEATURE_FLAGS.MULTI_POINT_PERSON_MODAL] &&
!!people.crossDataset?.length &&
people.seriesId !== undefined && (
<div className="data-point-selector">
<Select value={people.seriesId} onChange={(_id) => switchToDataPoint(_id)}>
{people.crossDataset.map((dataPoint) => (
<Select.Option
value={dataPoint.id}
key={`${dataPoint.action?.id}${dataPoint.breakdown_value}`}
>
<InsightLabel
seriesColor={colorList[dataPoint.id]}
action={dataPoint.action}
breakdownValue={
dataPoint.breakdown_value === ''
? 'None'
: dataPoint.breakdown_value?.toString()
}
showCountedByTag={showCountedByTag}
hasMultipleSeries={hasMultipleSeries}
/>
</Select.Option>
))}
</Select>
</div>
)}
<div className="user-count-subheader">
<IconPersonFilled style={{ fontSize: '1.125rem', marginRight: '0.5rem' }} />
<span>
This list contains{' '}
<b>
{people.count} unique {aggregationTargetLabel.plural}
</b>
{peopleParams?.pointValue !== undefined &&
(!peopleParams.action?.math || peopleParams.action?.math === 'total') && (
<>
{' '}
who performed the event{' '}
<b>
{peopleParams.pointValue} total{' '}
{pluralize(peopleParams.pointValue, 'time', undefined, false)}
</b>
</>
)}
.
</span>
</div>
{people.count > 0 ? (
<LemonTable
columns={
[
{
title: 'Person',
key: 'person',
render: function Render(_, actor: ActorType) {
return <ActorRow actor={actor} />
},
},
{
width: 0,
title: 'Recordings',
key: 'recordings',
render: function Render(_, actor: ActorType) {
if (
actor.matched_recordings?.length &&
actor.matched_recordings?.length > 0
) {
return (
<MultiRecordingButton
sessionRecordings={actor.matched_recordings}
onOpenRecording={(sessionRecording) => {
openRecordingModal(sessionRecording.session_id)
}}
/>
)
}
},
},
] as LemonTableColumns<ActorType>
}
className="persons-table"
rowKey="id"
expandable={{
expandedRowRender: function RenderPropertiesTable({ properties }) {
return Object.keys(properties).length ? (
<PropertiesTable properties={properties} />
) : (
'This person has no properties.'
)
},
}}
embedded
showHeader={false}
dataSource={people.people}
nouns={['person', 'persons']}
/>
) : (
<div className="person-row-container person-row">
We couldn't find any matching {aggregationTargetLabel.plural} for this data point.
</div>
)}
{people?.next && (
<div
style={{
margin: '1rem',
textAlign: 'center',
}}
>
<Button
type="primary"
style={{ color: 'white' }}
onClick={loadMorePeople}
loading={loadingMorePeople}
>
Load more {aggregationTargetLabel.plural}
</Button>
</div>
)}
</>
)
)}
</Modal>
</>
)
}
Example #23
Source File: EventsTable.tsx From posthog-foss with MIT License | 4 votes |
export function EventsTable({
fixedFilters,
pageKey,
hidePersonColumn,
hideTableConfig,
sceneUrl,
// Disables all interactivity and polling for filters
disableActions,
// How many months of data to fetch?
fetchMonths,
}: EventsTable): JSX.Element {
const { currentTeam } = useValues(teamLogic)
const logic = eventsTableLogic({
fixedFilters,
key: pageKey,
sceneUrl: sceneUrl || urls.events(),
disableActions,
fetchMonths,
})
const {
properties,
eventsFormatted,
isLoading,
hasNext,
isLoadingNext,
newEvents,
eventFilter,
automaticLoadEnabled,
exportUrl,
highlightEvents,
sceneIsEventsPage,
months,
} = useValues(logic)
const { tableWidth, selectedColumns } = useValues(tableConfigLogic)
const { propertyNames } = useValues(propertyDefinitionsModel)
const { fetchNextEvents, prependNewEvents, setEventFilter, toggleAutomaticLoad, startDownload, setPollingActive } =
useActions(logic)
const { filters } = useValues(propertyFilterLogic({ pageKey }))
const showLinkToPerson = !fixedFilters?.person_id
usePageVisibility(setPollingActive)
const newEventsRender = (
{ date_break, new_events }: EventsTableRowItem,
colSpan: number
): TableCellRepresentation => {
return {
children:
date_break ||
(new_events ? (
<LemonButton
icon={<IconSync />}
style={{ borderRadius: 0 }}
onClick={() => prependNewEvents()}
center
fullWidth
>
{newEvents.length === 1
? `There is 1 new event. Click here to load it`
: `There are ${newEvents.length || ''} new events. Click here to load them`}
</LemonButton>
) : (
'???'
)),
props: {
colSpan: colSpan + 1,
style: new_events ? { padding: 0 } : undefined,
},
}
}
const personColumn: LemonTableColumn<EventsTableRowItem, keyof EventsTableRowItem | undefined> = {
title: 'Person',
key: 'person',
render: function renderPerson(_, { event }: EventsTableRowItem) {
if (!event) {
return { props: { colSpan: 0 } }
}
return showLinkToPerson && event.person?.distinct_ids?.length ? (
<Link to={urls.person(event.person.distinct_ids[0])}>
<PersonHeader withIcon person={event.person} />
</Link>
) : (
<PersonHeader withIcon person={event.person} />
)
},
}
const defaultColumns = useMemo<LemonTableColumns<EventsTableRowItem>>(() => {
const _localColumns = [
{
title: 'Event',
key: 'event',
width: '16rem',
render: function render(_, item: EventsTableRowItem) {
if (!item.event) {
return newEventsRender(item, tableWidth)
}
const { event } = item
return <PropertyKeyInfo value={autoCaptureEventToDescription(event)} />
},
ellipsis: true,
},
{
title: 'URL / Screen',
key: 'url',
width: '4rem',
render: function renderURL(_, { event }: EventsTableRowItem) {
if (!event) {
return { props: { colSpan: 0 } }
}
const param = event.properties['$current_url'] ? '$current_url' : '$screen_name'
if (!disableActions) {
return (
<FilterPropertyLink
className="ph-no-capture"
property={param}
value={event.properties[param] as string}
filters={{ properties }}
/>
)
}
return <Property value={event.properties[param]} />
},
ellipsis: true,
},
{
title: 'Source',
key: 'source',
render: function renderSource(_, { event }: EventsTableRowItem) {
if (!event) {
return { props: { colSpan: 0 } }
}
if (!disableActions) {
return (
<FilterPropertyLink
property="$lib"
value={event.properties['$lib'] as string}
filters={{ properties }}
/>
)
}
return <Property value={event.properties['$lib']} />
},
},
] as LemonTableColumns<EventsTableRowItem>
if (!hidePersonColumn) {
_localColumns.splice(1, 0, personColumn)
}
return _localColumns
}, [tableWidth])
const columns = useMemo(() => {
const columnsSoFar =
selectedColumns === 'DEFAULT'
? defaultColumns
: selectedColumns.map(
(e, index): LemonTableColumn<EventsTableRowItem, keyof EventsTableRowItem | undefined> =>
defaultColumns.find((d) => d.key === e) || {
title: keyMapping['event'][e] ? keyMapping['event'][e].label : e,
key: e,
render: function render(_, item: EventsTableRowItem) {
const { event } = item
if (!event) {
if (index === 0) {
return newEventsRender(item, tableWidth)
} else {
return { props: { colSpan: 0 } }
}
}
if (!disableActions) {
return (
<FilterPropertyLink
className="ph-no-capture "
property={e}
value={event.properties[e] as string}
filters={{ properties }}
/>
)
}
return <Property value={event.properties[e]} />
},
}
)
columnsSoFar.push({
title: 'Time',
key: 'time',
render: function renderTime(_, { event }: EventsTableRowItem) {
if (!event) {
return { props: { colSpan: 0 } }
}
return <TZLabel time={event.timestamp} showSeconds />
},
})
columnsSoFar.push({
key: 'actions',
width: 0,
render: function renderActions(_, { event }: EventsTableRowItem) {
if (!event) {
return { props: { colSpan: 0 } }
}
let insightParams: Partial<FilterType> | undefined
if (event.event === '$pageview') {
insightParams = {
insight: InsightType.TRENDS,
interval: 'day',
display: ChartDisplayType.ActionsLineGraphLinear,
actions: [],
events: [
{
id: '$pageview',
name: '$pageview',
type: 'events',
order: 0,
properties: [
{
key: '$current_url',
value: event.properties.$current_url,
type: 'event',
},
],
},
],
}
} else if (event.event !== '$autocapture') {
insightParams = {
insight: InsightType.TRENDS,
interval: 'day',
display: ChartDisplayType.ActionsLineGraphLinear,
actions: [],
events: [
{
id: event.event,
name: event.event,
type: 'events',
order: 0,
properties: [],
},
],
}
}
return (
<More
overlay={
<>
{currentTeam && (
<LemonButton
type="stealth"
onClick={() =>
createActionFromEvent(
currentTeam.id,
event,
0,
currentTeam.data_attributes || []
)
}
fullWidth
data-attr="events-table-create-action"
>
Create action from event
</LemonButton>
)}
{insightParams && (
<LemonButton
type="stealth"
to={urls.insightNew(insightParams)}
fullWidth
data-attr="events-table-usage"
>
Try out in Insights
</LemonButton>
)}
</>
}
/>
)
},
})
return columnsSoFar
}, [selectedColumns])
return (
<div data-attr="manage-events-table">
<div className="events" data-attr="events-table">
{!disableActions && <EventPageHeader activeTab={EventsTab.Events} hideTabs={!sceneIsEventsPage} />}
{!disableActions && (
<div
className="mb"
style={{
display: 'flex',
gap: '0.75rem',
flexWrap: 'wrap',
justifyContent: 'space-between',
alignItems: 'start',
}}
>
<div style={{ display: 'flex', gap: '0.75rem', flexDirection: 'column', flexGrow: 1 }}>
<EventName
value={eventFilter}
onChange={(value: string) => {
setEventFilter(value || '')
}}
/>
<PropertyFilters
pageKey={pageKey}
style={{ marginBottom: 0 }}
eventNames={eventFilter ? [eventFilter] : []}
/>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '0.75rem' }}>
<LemonSwitch
id="autoload-switch"
label="Automatically load new events"
checked={automaticLoadEnabled}
onChange={toggleAutomaticLoad}
/>
{!hideTableConfig && (
<TableConfig
availableColumns={propertyNames}
immutableColumns={['event', 'person']}
defaultColumns={defaultColumns.map((e) => e.key || '')}
/>
)}
{exportUrl && (
<Tooltip title="Export up to 10,000 latest events." placement="left">
<Button icon={<DownloadOutlined />} onClick={startDownload}>
Export events
</Button>
</Tooltip>
)}
</div>
</div>
)}
<LemonTable
dataSource={eventsFormatted}
loading={isLoading}
columns={columns}
key={selectedColumns === 'DEFAULT' ? 'default' : selectedColumns.join('-')}
className="ph-no-capture"
emptyState={
isLoading ? undefined : filters.some((filter) => Object.keys(filter).length) || eventFilter ? (
`No events matching filters found in the last ${months} months!`
) : (
<>
This project doesn't have any events. If you haven't integrated PostHog yet,{' '}
<Link to="/project/settings">
click here to instrument analytics with PostHog in your product
</Link>
.
</>
)
}
rowKey={(row) =>
row.event ? row.event.id + '-' + row.event.event : row.date_break?.toString() || ''
}
rowClassName={(row) => {
return clsx({
'event-row': row.event,
highlighted: row.event && highlightEvents[row.event.id],
'event-row-is-exception': row.event && row.event.event === '$exception',
'event-row-date-separator': row.date_break,
'event-row-new': row.new_events,
})
}}
expandable={{
expandedRowRender: function renderExpand({ event }) {
return event && <EventDetails event={event} />
},
rowExpandable: ({ event, date_break, new_events }) => (date_break || new_events ? -1 : !!event),
}}
/>
<Button
type="primary"
onClick={fetchNextEvents}
loading={isLoadingNext}
style={{
display: hasNext || isLoadingNext ? 'block' : 'none',
margin: '2rem auto 1rem',
}}
>
Load more events
</Button>
</div>
</div>
)
}
Example #24
Source File: palette.tsx From jmix-frontend with Apache License 2.0 | 4 votes |
palette = () =>
<Palette>
<Category name="Text">
<Component name="Formatted Message">
<Variant>
<FormattedMessage />
</Variant>
</Component>
<Component name="Heading">
<Variant name='h1'>
<Typography.Title></Typography.Title>
</Variant>
<Variant name='h2'>
<Typography.Title level = {2}></Typography.Title>
</Variant>
<Variant name='h3'>
<Typography.Title level = {3}></Typography.Title>
</Variant>
<Variant name='h4'>
<Typography.Title level = {4}></Typography.Title>
</Variant>
<Variant name='h5'>
<Typography.Title level = {5}></Typography.Title>
</Variant>
</Component>
<Component name='Text'>
<Variant>
<Typography.Text></Typography.Text>
</Variant>
<Variant name = 'Secondary'>
<Typography.Text type="secondary"></Typography.Text>
</Variant>
<Variant name = 'Success'>
<Typography.Text type="success"></Typography.Text>
</Variant>
<Variant name = 'Warning'>
<Typography.Text type="warning"></Typography.Text>
</Variant>
<Variant name = 'Danger'>
<Typography.Text type="danger"></Typography.Text>
</Variant>
<Variant name = 'Disabled'>
<Typography.Text disabled></Typography.Text>
</Variant>
</Component>
</Category>
<Category name="Layout">
<Component name="Divider">
<Variant>
<Divider />
</Variant>
</Component>
<Component name="Grid">
<Variant name="Simple Row">
<Row></Row>
</Variant>
<Variant name="Two columns">
<Row>
<Col span={12}></Col>
<Col span={12}></Col>
</Row>
</Variant>
<Variant name="Three columns">
<Row>
<Col span={8}></Col>
<Col span={8}></Col>
<Col span={8}></Col>
</Row>
</Variant>
</Component>
<Component name="Space">
<Variant>
<Space />
</Variant>
<Variant name="Small">
<Space size={"small"} />
</Variant>
<Variant name="Large">
<Space size={"large"} />
</Variant>
</Component>
</Category>
<Category name="Controls">
<Component name="Autocomplete">
<Variant>
<AutoComplete placeholder="input here" />
</Variant>
</Component>
<Component name="Button">
<Variant>
<Button></Button>
</Variant>
<Variant name="Primary">
<Button type="primary" ></Button>
</Variant>
<Variant name="Link">
<Button type="link" ></Button>
</Variant>
<Variant name="Dropdown">
<Dropdown
trigger={['click']}
overlay={<Menu>
<Menu.Item>
</Menu.Item>
<Menu.Item>
</Menu.Item>
<Menu.Item>
</Menu.Item>
</Menu>}
>
<Button></Button>
</Dropdown>
</Variant>
</Component>
<Component name="Checkbox">
<Variant>
<Checkbox />
</Variant>
</Component>
<Component name='Switch'>
<Variant>
<Switch />
</Variant>
</Component>
<Component name='Radio Group'>
<Variant>
<Radio.Group>
<Radio value={1}>A</Radio>
<Radio value={2}>B</Radio>
<Radio value={3}>C</Radio>
<Radio value={4}>D</Radio>
</Radio.Group>
</Variant>
<Variant name = 'Button'>
<Radio.Group>
<Radio.Button value={1}>A</Radio.Button>
<Radio.Button value={2}>B</Radio.Button>
<Radio.Button value={3}>C</Radio.Button>
<Radio.Button value={4}>D</Radio.Button>
</Radio.Group>
</Variant>
</Component>
<Component name="DatePicker">
<Variant>
<DatePicker />
</Variant>
<Variant name="Range">
<DatePicker.RangePicker />
</Variant>
</Component>
<Component name="TimePicker">
<Variant>
<TimePicker />
</Variant>
<Variant name="Range">
<TimePicker.RangePicker />
</Variant>
</Component>
<Component name="Input">
<Variant>
<Input />
</Variant>
<Variant name='Number'>
<InputNumber />
</Variant>
</Component>
<Component name='Select'>
<Variant>
<Select defaultValue="1">
<Select.Option value="1">1</Select.Option>
<Select.Option value="2">2</Select.Option>
</Select>
</Variant>
<Variant name='Multiple'>
<Select
defaultValue={["1"]}
mode="multiple"
allowClear
>
<Select.Option value="1">1</Select.Option>
<Select.Option value="2">2</Select.Option>
</Select>
</Variant>
</Component>
<Component name="Link">
<Variant>
<Typography.Link href="" target="_blank">
</Typography.Link>
</Variant>
</Component>
<Component name='Slider'>
<Variant>
<Slider defaultValue={30} />
</Variant>
<Variant name = 'Range'>
<Slider range defaultValue={[20, 50]}/>
</Variant>
</Component>
</Category>
<Category name="Data Display">
<Component name="Field">
<Variant>
<Field
entityName={ENTITY_NAME}
disabled={readOnlyMode}
propertyName=''
formItemProps={{
style: { marginBottom: "12px" }
}}
/>
</Variant>
</Component>
<Component name="Card">
<Variant>
<Card />
</Variant>
<Variant name="With Title">
<Card>
<Card title="Card title">
<p>Card content</p>
</Card>
</Card>
</Variant>
<Variant name="My custom card">
<Card>
<Card title="Card title">
<p>Card content</p>
<Avatar />
</Card>
</Card>
</Variant>
</Component>
<Component name="Tabs">
<Variant>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="Tab 1" key="1">
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 2" key="2">
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 3" key="3">
Content of Tab Pane 3
</Tabs.TabPane>
</Tabs>
</Variant>
<Variant name = "Tab Pane">
<Tabs.TabPane>
</Tabs.TabPane>
</Variant>
</Component>
<Component name="Collapse">
<Variant>
<Collapse defaultActiveKey='1'>
<Collapse.Panel header="This is panel header 1" key="1">
</Collapse.Panel>
<Collapse.Panel header="This is panel header 2" key="2">
</Collapse.Panel>
<Collapse.Panel header="This is panel header 3" key="3">
</Collapse.Panel>
</Collapse>
</Variant>
</Component>
<Component name="Image">
<Variant>
<Image
width={200}
src=""
/>
</Variant>
</Component>
<Component name="Avatar">
<Variant>
<Avatar icon={<UserOutlined />} />
</Variant>
<Variant name="Image">
<Avatar src="https://joeschmoe.io/api/v1/random" />
</Variant>
</Component>
<Component name="Badge">
<Variant>
<Badge count={1}>
</Badge>
</Variant>
</Component>
<Component name="Statistic">
<Variant>
<Statistic title="Title" value={112893} />
</Variant>
</Component>
<Component name="Alert">
<Variant name="Success">
<Alert message="Text" type="success" />
</Variant>
<Variant name="Info">
<Alert message="Text" type="info" />
</Variant>
<Variant name="Warning">
<Alert message="Text" type="warning" />
</Variant>
<Variant name="Error">
<Alert message="Text" type="error" />
</Variant>
</Component>
<Component name='List'>
<Variant>
<List
bordered
dataSource={[]}
renderItem={item => (
<List.Item>
</List.Item>
)}
/>
</Variant>
</Component>
</Category>
<Category name="Icons">
<Component name="Arrow">
<Variant name = 'Up'>
<ArrowUpOutlined />
</Variant>
<Variant name = 'Down'>
<ArrowDownOutlined />
</Variant>
<Variant name = 'Left'>
<ArrowLeftOutlined />
</Variant>
<Variant name = 'Right'>
<ArrowRightOutlined />
</Variant>
</Component>
<Component name = 'Question'>
<Variant>
<QuestionOutlined />
</Variant>
<Variant name = 'Circle'>
<QuestionCircleOutlined />
</Variant>
</Component>
<Component name = 'Plus'>
<Variant>
<PlusOutlined />
</Variant>
<Variant name = 'Circle'>
<PlusCircleOutlined />
</Variant>
</Component>
<Component name = 'Info'>
<Variant>
<InfoOutlined />
</Variant>
<Variant name = 'Circle'>
<InfoCircleOutlined />
</Variant>
</Component>
<Component name = 'Exclamation'>
<Variant>
<ExclamationOutlined />
</Variant>
<Variant name = 'Circle'>
<ExclamationCircleOutlined />
</Variant>
</Component>
<Component name = 'Close'>
<Variant>
<CloseOutlined />
</Variant>
<Variant name = 'Circle'>
<CloseCircleOutlined />
</Variant>
</Component>
<Component name = 'Check'>
<Variant>
<CheckOutlined />
</Variant>
<Variant name = 'Circle'>
<CheckCircleOutlined />
</Variant>
</Component>
<Component name = 'Edit'>
<Variant>
<EditOutlined />
</Variant>
</Component>
<Component name = 'Copy'>
<Variant>
<CopyOutlined />
</Variant>
</Component>
<Component name = 'Delete'>
<Variant>
<DeleteOutlined />
</Variant>
</Component>
<Component name = 'Bars'>
<Variant>
<BarsOutlined />
</Variant>
</Component>
<Component name = 'Bell'>
<Variant>
<BellOutlined />
</Variant>
</Component>
<Component name = 'Clear'>
<Variant>
<ClearOutlined />
</Variant>
</Component>
<Component name = 'Download'>
<Variant>
<DownloadOutlined />
</Variant>
</Component>
<Component name = 'Upload'>
<Variant>
<UploadOutlined />
</Variant>
</Component>
<Component name = 'Sync'>
<Variant>
<SyncOutlined />
</Variant>
</Component>
<Component name = 'Save'>
<Variant>
<SaveOutlined />
</Variant>
</Component>
<Component name = 'Search'>
<Variant>
<SearchOutlined />
</Variant>
</Component>
<Component name = 'Settings'>
<Variant>
<SettingOutlined />
</Variant>
</Component>
<Component name = 'Paperclip'>
<Variant>
<PaperClipOutlined />
</Variant>
</Component>
<Component name = 'Phone'>
<Variant>
<PhoneOutlined />
</Variant>
</Component>
<Component name = 'Mail'>
<Variant>
<MailOutlined />
</Variant>
</Component>
<Component name = 'Home'>
<Variant>
<HomeOutlined />
</Variant>
</Component>
<Component name = 'Contacts'>
<Variant>
<ContactsOutlined />
</Variant>
</Component>
<Component name = 'User'>
<Variant>
<UserOutlined />
</Variant>
<Variant name = 'Add'>
<UserAddOutlined />
</Variant>
<Variant name = 'Remove'>
<UserDeleteOutlined />
</Variant>
</Component>
<Component name = 'Team'>
<Variant>
<TeamOutlined />
</Variant>
</Component>
</Category>
</Palette>