@ant-design/icons#MenuUnfoldOutlined TypeScript Examples
The following examples show how to use
@ant-design/icons#MenuUnfoldOutlined.
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: Header.tsx From nodestatus with MIT License | 6 votes |
Header: FC<Props> = props => {
const navigate = useNavigate();
const { isCollapsed, toggleCollapsed } = props.collapsed;
const menu = (
<Menu
items={[
{
key: 'logout',
label: 'Logout',
icon: <LogoutOutlined className="mr-2 align-middle" />,
className: 'align-middle'
}
]}
onClick={({ key }) => {
if (key === 'logout') {
localStorage.removeItem('token');
navigate('/login');
}
}}
/>
);
return (
<div className="h-full flex items-center justify-between">
{React.createElement(isCollapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
className: 'text-2xl',
onClick: toggleCollapsed
})}
<Dropdown overlay={menu} placement="bottom">
<Avatar size={40} icon={<UserOutlined />} />
</Dropdown>
</div>
);
}
Example #2
Source File: index.tsx From memex with MIT License | 6 votes |
BLOCK_TYPES = [
{ label: 'H1', style: 'header-one' },
{ label: 'H2', style: 'header-two' },
{ label: 'H3', style: 'header-three' },
// {label: 'H4', style: 'header-four'},
// {label: 'H5', style: 'header-five'},
// {label: 'H6', style: 'header-six'},
{ label: 'Blockquote', style: 'blockquote', icon: <MenuUnfoldOutlined /> },
{
label: 'Unordered List',
style: 'unordered-list-item',
icon: <UnorderedListOutlined />,
},
{
label: 'Ordered List',
style: 'ordered-list-item',
icon: <OrderedListOutlined />,
},
{ label: 'Code Block', style: 'code-block', icon: <CodeOutlined /> },
]
Example #3
Source File: index.tsx From shippo with MIT License | 5 votes |
defaultRenderCollapsedButton = (collapsed?: boolean) =>
collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />
Example #4
Source File: index.tsx From shippo with MIT License | 5 votes |
defaultRenderCollapsedButton = (collapsed?: boolean) =>
collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />
Example #5
Source File: Layout.tsx From yugong with MIT License | 5 votes |
MainLayout: React.FC<Props> = ({ children }) => {
const [collapsed, setCollapsed] = useState(true);
let location = useLocation();
const history = useHistory();
const { auth } = useSelector((state: RootState) => state.controller);
const { loginOut } = useDispatch<Dispatch>().controller;
return (
<Layout>
<Sider
theme="light"
className={s.side}
trigger={null}
collapsible
collapsed={collapsed}
>
<div className={s.logo}>
<GroupOutlined /> {collapsed ? "" : "YuGong"}
</div>
<Menu
theme="light"
mode="inline"
defaultSelectedKeys={[location.pathname]}
>
{menus.map((item) => (
<Menu.Item key={item.path} icon={item.icon}>
{item.name}
<Link to={item.path || "/"} />
</Menu.Item>
))}
</Menu>
</Sider>
<Layout className="site-layout">
<Header className={s.layout} style={{ padding: 0 }}>
{React.createElement(
collapsed ? MenuUnfoldOutlined : MenuFoldOutlined,
{
className: s.trigger,
onClick: () => setCollapsed(!collapsed),
}
)}
<div className={s.auto} />
<div className={s.auth}>
{auth?.isLogin ? (
<div>
<Avatar size="small" icon={<UserOutlined />} />
{auth.session?.username}
<Button type="link" onClick={loginOut}>退出</Button>
</div>
) : (
<>
{location.pathname !== '/login' ? <Button type="link" onClick={() => history.push('/login')}>登录</Button> : null}
{location.pathname !== '/register' ? <Button type="link" onClick={() => history.push('/register')}>注册</Button> : null}
</>
)}
</div>
</Header>
<Content className={s.content}>{children}</Content>
</Layout>
</Layout>
);
}
Example #6
Source File: Header.tsx From react_admin with MIT License | 4 votes |
Headers: React.FC<Iprops> = (props) => {
const { collapsed } = props;
const [fullScreen, setFullScreen] = useState(false); // 当前是否是全屏状态
// 进入全屏
const requestFullScreen = useCallback(() => {
const element: HTMLElement & Element = document.documentElement;
// 判断各种浏览器,找到正确的方法
const requestMethod =
element.requestFullscreen || // W3C
element.webkitRequestFullscreen || // Chrome等
element.mozRequestFullScreen || // FireFox
element.msRequestFullscreen; // IE11
if (requestMethod) {
requestMethod.call(element);
}
setFullScreen(true);
}, []);
// 退出登录
const onMenuClick = useCallback(
(e) => {
// 退出按钮被点击
if (e.key === "logout") {
props.onLogout();
}
},
[props]
);
// 退出全屏
const exitFullScreen = useCallback(() => {
// 判断各种浏览器,找到正确的方法
const element: Document & Element = document;
const exitMethod =
element.exitFullscreen || // W3C
element.mozCancelFullScreen || // firefox
element.webkitExitFullscreen || // Chrome等
element.msExitFullscreen; // IE11
if (exitMethod) {
exitMethod.call(document);
}
setFullScreen(false);
}, []);
const toggle = () => {
props.setColl(!collapsed);
};
return (
<Header className="site-layout-background header" style={{ padding: 0 }}>
<Tooltip title={props.collapsed ? "展开菜单" : "收起菜单"}>
{React.createElement(
collapsed ? MenuUnfoldOutlined : MenuFoldOutlined,
{
className: "trigger",
onClick: toggle,
}
)}
</Tooltip>
<div className="rightBox">
<Tooltip placement="bottom" title={fullScreen ? "退出全屏" : "全屏"}>
<div className="full all_center">
{fullScreen ? (
<FullscreenExitOutlined
className="icon"
onClick={exitFullScreen}
/>
) : (
<FullscreenOutlined
className="icon"
onClick={requestFullScreen}
/>
)}
</div>
</Tooltip>
<Dropdown
overlay={
<Menu className="menu" selectedKeys={[]} onClick={onMenuClick}>
<Menu.Item key="user">
<Link to="/home/user/admin">
<UserOutlined />
个人中心
</Link>
</Menu.Item>
<Menu.Item key="message">
<Link to="/home/user/level">
<MessageOutlined />
个人设置
</Link>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="logout">
<LogoutOutlined />
退出登录
</Menu.Item>
</Menu>
}
placement="bottomRight"
>
<div className="userhead all_center">
<SmileOutlined />
<span className="username">admin</span>
</div>
</Dropdown>
</div>
</Header>
);
}
Example #7
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 #8
Source File: GraphFilterView.tsx From dendron with GNU Affero General Public License v3.0 | 4 votes |
GraphFilterView = ({
config,
updateConfigField,
isGraphReady,
customCSS,
type,
}: FilterProps) => {
const [showView, setShowView] = useState(false);
const { currentTheme } = useCurrentTheme();
if (!currentTheme) return <></>;
const isVisible = showView && isGraphReady;
return (
<Space
direction="vertical"
style={{
zIndex: 10,
position: "absolute",
top: AntThemes[currentTheme].graph.filterView.margin,
left: AntThemes[currentTheme].graph.filterView.margin,
borderRadius: AntThemes[currentTheme].graph.filterView.borderRadius,
minWidth: AntThemes[currentTheme].graph.filterView.minWidth,
}}
>
<Tooltip
title={`${isVisible ? "Hide" : "Show"} Graph Configuration`}
placement="right"
>
<Button
type="primary"
shape="circle"
icon={isVisible ? <MenuFoldOutlined /> : <MenuUnfoldOutlined />}
onClick={() => setShowView((v) => !v)}
style={{
opacity: isGraphReady ? 1 : 0,
transform: "0.2s opacity ease-in-out",
}}
/>
</Tooltip>
<Collapse
style={{
background: AntThemes[currentTheme].graph.filterView.background,
display: isVisible ? "block" : "none",
}}
>
<Panel header="Vaults" key="vaults">
<FilterViewSection
section="vaults"
config={config}
updateConfigField={updateConfigField}
/>
</Panel>
<Panel header="Connections" key="connections">
<FilterViewSection
section="connections"
config={config}
updateConfigField={updateConfigField}
/>
</Panel>
<Panel header="Filter" key="filter">
<FilterViewSection
section="filter"
config={config}
updateConfigField={updateConfigField}
/>
</Panel>
<Panel header="Options" key="options">
<FilterViewSection
section="options"
config={config}
updateConfigField={updateConfigField}
/>
</Panel>
<Panel header="Information" key="information">
<FilterViewSection
section="information"
config={config}
updateConfigField={updateConfigField}
/>
</Panel>
{type === "note" && (
<Panel header="Graph Theme" key="graphTheme">
<FilterViewSection
section="graphTheme"
config={config}
updateConfigField={updateConfigField}
customCSS={customCSS}
/>
</Panel>
)}
</Collapse>
</Space>
);
}
Example #9
Source File: index.tsx From fe-v5 with Apache License 2.0 | 4 votes |
SideMenu: FC = () => {
const { t, i18n } = useTranslation();
const menuList = [
{
key: 'targets',
icon: <DatabaseOutlined />,
title: t('监控对象'),
children: [
{
key: '/targets',
title: t('对象列表'),
},
],
},
{
key: 'monitor',
icon: <LineChartOutlined />,
title: t('监控看图'),
children: [
{
key: '/metric/explorer',
title: t('即时查询'),
},
{
key: '/object/explorer',
title: t('快捷视图'),
},
{
key: '/dashboards',
title: t('监控大盘'),
},
],
},
{
key: 'alarm',
icon: <AlertOutlined />,
title: t('告警管理'),
children: [
{
key: '/alert-rules',
title: t('告警规则'),
},
{
key: '/alert-mutes',
title: t('屏蔽规则'),
},
{
key: '/alert-subscribes',
title: t('订阅规则'),
},
{
key: '/alert-cur-events',
title: t('活跃告警'),
},
{
key: '/alert-his-events',
title: t('历史告警'),
},
],
},
{
key: 'job',
icon: <CodeOutlined />,
title: t('告警自愈'),
children: [
{
key: '/job-tpls',
title: t('自愈脚本'),
},
{
key: '/job-tasks',
title: t('执行历史'),
},
],
},
{
key: 'manage',
icon: <UserOutlined />,
title: t('人员组织'),
children: [
{
key: '/users',
title: t('用户管理'),
},
{
key: '/user-groups',
title: t('团队管理'),
},
{
key: '/busi-groups',
title: t('业务组管理'),
},
],
},
{
key: 'help',
icon: <Icon component={SystemInfoSvg as any} />,
title: t('系统信息'),
children: [
{
key: '/help/version',
title: t('系统版本'),
},
{
key: '/help/contact',
title: t('联系我们'),
},
{
key: '/help/migrate',
title: t('管理员迁移'),
},
],
},
];
const [menus, setMenus] = useState(menuList);
const history = useHistory();
const location = useLocation();
const { pathname } = location;
let { profile } = useSelector<AccountRootState, accountStoreState>((state) => state.account);
const [collapsed, setCollapsed] = useState(localStorage.getItem('menuCollapsed') === '1');
const [selectedKeys, setSelectedKeys] = useState<string[]>([defaultSelectedKey(menus, pathname)]);
const [openKeys, setOpenKeys] = useState<string[]>(collapsed ? [] : [getDefaultOpenKey(menus, pathname)]);
const toggleCollapsed = () => {
setCollapsed(!collapsed);
localStorage.setItem('menuCollapsed', !collapsed ? '1' : '0');
};
const handleClick: MenuClickEventHandler = ({ key }) => {
setSelectedKeys([key as string]);
// 写两个key as string 感觉有点傻
if (key === 'changeLanguage') {
let language = i18n.language == 'en' ? 'zh' : 'en';
i18n.changeLanguage(language);
localStorage.setItem('language', language);
}
if ((key as string).startsWith('/')) {
history.push(key as string);
}
};
const hideSideMenu = () => location.pathname === '/login' || location.pathname.startsWith('/chart/') || location.pathname === '/callback';
useEffect(() => {
setSelectedKeys([defaultSelectedKey(menus, pathname)]);
if (!collapsed) {
setOpenKeys(_.union([...openKeys, getDefaultOpenKey(menus, pathname)]));
}
}, [pathname, collapsed]);
useEffect(() => {
if (profile.roles.length > 0) {
if (profile.roles.indexOf('Admin') === -1) {
getMenuPerm().then((res) => {
const { dat } = res;
const newMenus = [...menuList];
newMenus.forEach((menu) => {
menu.children = menu.children.filter((item) => dat.includes(item.key));
});
setMenus(newMenus);
});
} else {
setMenus(menuList);
}
}
}, [profile.roles]);
return hideSideMenu() ? null : (
<div
style={{
display: 'flex',
flexDirection: 'column',
}}
>
<div className={`home ${collapsed ? 'collapse' : ''}`}>
<div className='name' onClick={() => history.push('/metric/explorer')} key='overview'>
<img src={collapsed ? '/image/logo.svg' : '/image/logo-l.svg'} alt='' className='logo' />
</div>
</div>
<Menu
className='left-menu-container'
theme='dark'
inlineCollapsed={collapsed}
openKeys={openKeys}
selectedKeys={selectedKeys}
onClick={handleClick}
mode='inline'
onOpenChange={(openKeys: string[]) => {
setOpenKeys(openKeys);
}}
>
{_.map(menus, (subMenus) => {
return (
subMenus.children.length > 0 && (
<SubMenu key={subMenus.key} icon={subMenus.icon} title={subMenus.title}>
{_.map(subMenus.children, (menu) => {
return <Menu.Item key={menu.key}>{menu.title}</Menu.Item>;
})}
</SubMenu>
)
);
})}
{lazyMenu.sort((a, b) => b.weight - a.weight).map((item) => item.content)}
</Menu>
<Button type='text' onClick={toggleCollapsed} className='collapseBtn'>
{React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined)}
</Button>
</div>
);
}
Example #10
Source File: FullScreenPanel.tsx From datart with Apache License 2.0 | 4 votes |
FullScreenPanel: React.FC<{}> = memo(() => {
const { boardId, boardType } = useContext(BoardContext);
const dispatch = useDispatch();
const itemId = useSelector((state: { board: BoardState }) =>
makeSelectBoardFullScreenPanelById()(state, boardId),
);
const widgetMap = useSelector((state: { board: BoardState }) =>
selectBoardWidgetMapById(state, boardId),
);
const widgets = useMemo(() => {
return Object.values(widgetMap).filter(item =>
CanFullScreenWidgetTypes.includes(item.config.type),
);
}, [widgetMap]);
const [collapsed, setCollapsed] = useState(false);
const toggle = useCallback(() => {
setCollapsed(c => !c);
}, []);
const closeFullScreen = useCallback(() => {
setCollapsed(c => false);
dispatch(boardActions.updateFullScreenPanel({ boardId, itemId: '' }));
}, [boardId, dispatch]);
const changeItem = useCallback(
e => {
dispatch(
boardActions.updateFullScreenPanel({
boardId,
itemId: e.key,
}),
);
},
[boardId, dispatch],
);
const chart = useMemo(() => {
if (!itemId) return null;
const widget = widgetMap[itemId];
if (widget) {
return (
<WidgetWrapProvider
id={widget.id}
boardEditing={false}
boardId={boardId}
>
<FullScreenWidgetMapper boardEditing={false} boardType={boardType} />
</WidgetWrapProvider>
);
}
}, [boardId, boardType, itemId, widgetMap]);
return (
<>
{itemId && (
<FullScreenWrap show={collapsed}>
<FullHeader>
<div onClick={toggle}>
{collapsed ? <MenuFoldOutlined /> : <MenuUnfoldOutlined />}
<span>{widgetMap[itemId].config.name}</span>
</div>
<Button className="close-fullscreen" onClick={closeFullScreen}>
取消全屏
</Button>
</FullHeader>
<div className="full-container">
{chart}
{itemId && (
<div className="full-menu">
<Menu
mode="inline"
onClick={changeItem}
defaultSelectedKeys={[itemId]}
>
{widgets.map(ele => (
<Menu.Item key={ele.id}>{ele.config.name}</Menu.Item>
))}
</Menu>
</div>
)}
</div>
</FullScreenWrap>
)}
</>
);
})
Example #11
Source File: index.tsx From datart with Apache License 2.0 | 4 votes |
Sidebar = memo(
({ isDragging, width, sliderVisible, handleSliderVisible }: SidebarProps) => {
const history = useHistory();
const dispatch = useDispatch();
const { showSaveForm } = useContext(SaveFormContext);
const orgId = useSelector(selectOrgId);
const selectViewTree = useMemo(makeSelectViewTree, []);
const viewsData = useSelector(selectViews);
const t = useI18NPrefix('view.sidebar');
const getIcon = useCallback(
({ isFolder }: ViewSimpleViewModel) =>
isFolder ? (
p => (p.expanded ? <FolderOpenFilled /> : <FolderFilled />)
) : (
<CodeFilled />
),
[],
);
const getDisabled = useCallback(
({ deleteLoading }: ViewSimpleViewModel) => deleteLoading,
[],
);
const treeData = useSelector(state =>
selectViewTree(state, { getIcon, getDisabled }),
);
const { filteredData: filteredTreeData, debouncedSearch: treeSearch } =
useDebouncedSearch(treeData, (keywords, d) =>
d.title.toLowerCase().includes(keywords.toLowerCase()),
);
const archived = useSelector(selectArchived);
const recycleList = useMemo(
() =>
archived?.map(({ id, name, parentId, isFolder, deleteLoading }) => ({
key: id,
title: name,
parentId,
icon: isFolder ? <FolderOutlined /> : <FileOutlined />,
isFolder,
disabled: deleteLoading,
})),
[archived],
);
const { filteredData: filteredListData, debouncedSearch: listSearch } =
useDebouncedSearch(recycleList, (keywords, d) =>
d.title.toLowerCase().includes(keywords.toLowerCase()),
);
const add = useCallback(
({ key }) => {
switch (key) {
case 'view':
history.push(
`/organizations/${orgId}/views/${`${UNPERSISTED_ID_PREFIX}${uuidv4()}`}`,
);
break;
case 'folder':
showSaveForm({
type: CommonFormTypes.Add,
visible: true,
simple: true,
parentIdLabel: t('parent'),
onSave: (values, onClose) => {
let index = getInsertedNodeIndex(values, viewsData);
dispatch(
saveFolder({
folder: {
...values,
parentId: values.parentId || null,
index,
},
resolve: onClose,
}),
);
},
});
break;
default:
break;
}
},
[dispatch, history, orgId, showSaveForm, viewsData, t],
);
const titles = useMemo(
() => [
{
key: 'list',
title: t('title'),
search: true,
add: {
items: [
{ key: 'view', text: t('addView') },
{ key: 'folder', text: t('addFolder') },
],
callback: add,
},
more: {
items: [
{
key: 'recycle',
text: t('recycle'),
prefix: <DeleteOutlined className="icon" />,
},
{
key: 'collapse',
text: t(sliderVisible ? 'open' : 'close'),
prefix: sliderVisible ? (
<MenuUnfoldOutlined className="icon" />
) : (
<MenuFoldOutlined className="icon" />
),
},
],
callback: (key, _, onNext) => {
switch (key) {
case 'recycle':
onNext();
break;
case 'collapse':
handleSliderVisible(!sliderVisible);
break;
}
},
},
onSearch: treeSearch,
},
{
key: 'recycle',
title: t('recycle'),
back: true,
search: true,
onSearch: listSearch,
},
],
[add, treeSearch, listSearch, t, handleSliderVisible, sliderVisible],
);
return (
<Wrapper
sliderVisible={sliderVisible}
className={sliderVisible ? 'close' : ''}
isDragging={isDragging}
width={width}
>
{sliderVisible ? (
<MenuUnfoldOutlined className="menuUnfoldOutlined" />
) : (
<></>
)}
<ListNavWrapper defaultActiveKey="list">
<ListPane key="list">
<ListTitle {...titles[0]} />
<FolderTree treeData={filteredTreeData} />
</ListPane>
<ListPane key="recycle">
<ListTitle {...titles[1]} />
<Recycle list={filteredListData} />
</ListPane>
</ListNavWrapper>
</Wrapper>
);
},
)
Example #12
Source File: index.tsx From datart with Apache License 2.0 | 4 votes |
Folders = memo(
({
selectedId,
className,
i18nPrefix,
sliderVisible,
handleSliderVisible,
}: FoldersProps) => {
const dispatch = useDispatch();
const orgId = useSelector(selectOrgId);
const selectVizTree = useMemo(makeSelectVizTree, []);
const t = useI18NPrefix(i18nPrefix);
const history = useHistory();
const { showSaveForm } = useContext(SaveFormContext);
const addVizFn = useAddViz({ showSaveForm });
const getIcon = useGetVizIcon();
const getDisabled = useCallback(
({ deleteLoading }: FolderViewModel) => deleteLoading,
[],
);
const treeData = useSelector(state =>
selectVizTree(state, { getIcon, getDisabled }),
);
const { filteredData: filteredTreeData, debouncedSearch: treeSearch } =
useDebouncedSearch(treeData, (keywords, d) =>
d.title.toLowerCase().includes(keywords.toLowerCase()),
);
const archivedDatacharts = useSelector(selectArchivedDatacharts);
const archivedDashboards = useSelector(selectArchivedDashboards);
const archivedDataChartLoading = useSelector(
selectArchivedDatachartLoading,
);
const archivedDashboardLoading = useSelector(
selectArchivedDashboardLoading,
);
const { filteredData: filteredListData, debouncedSearch: listSearch } =
useDebouncedSearch(
archivedDatacharts.concat(archivedDashboards),
(keywords, d) => d.name.toLowerCase().includes(keywords.toLowerCase()),
);
const recycleInit = useCallback(() => {
dispatch(getArchivedDatacharts(orgId));
dispatch(getArchivedDashboards(orgId));
}, [dispatch, orgId]);
const add = useCallback(
({ key }) => {
if (key === 'DATACHART') {
history.push({
pathname: `/organizations/${orgId}/vizs/chartEditor`,
search: `dataChartId=&chartType=dataChart&container=dataChart`,
});
return false;
}
addVizFn({
vizType: key,
type: CommonFormTypes.Add,
visible: true,
initialValues: undefined,
});
},
[orgId, history, addVizFn],
);
const titles = useMemo(
() => [
{
subTitle: t('folders.folderTitle'),
add: {
items: [
{ key: 'DATACHART', text: t('folders.startAnalysis') },
{ key: 'DASHBOARD', text: t('folders.dashboard') },
{ key: 'FOLDER', text: t('folders.folder') },
],
callback: add,
},
more: {
items: [
{
key: 'recycle',
text: t('folders.recycle'),
prefix: <DeleteOutlined className="icon" />,
},
{
key: 'collapse',
text: t(sliderVisible ? 'folders.open' : 'folders.close'),
prefix: sliderVisible ? (
<MenuUnfoldOutlined className="icon" />
) : (
<MenuFoldOutlined className="icon" />
),
},
],
callback: (key, _, onNext) => {
switch (key) {
case 'recycle':
onNext();
break;
case 'collapse':
handleSliderVisible(!sliderVisible);
dispatchResize();
break;
}
},
},
search: true,
onSearch: treeSearch,
},
{
key: 'recycle',
subTitle: t('folders.recycle'),
back: true,
search: true,
onSearch: listSearch,
},
],
[add, treeSearch, listSearch, t, sliderVisible, handleSliderVisible],
);
return (
<Wrapper className={className} defaultActiveKey="list">
<ListPane key="list">
<ListTitle {...titles[0]} />
<FolderTree
treeData={filteredTreeData}
selectedId={selectedId}
i18nPrefix={i18nPrefix}
/>
</ListPane>
<ListPane key="recycle">
<ListTitle {...titles[1]} />
<Recycle
type="viz"
orgId={orgId}
list={filteredListData}
listLoading={archivedDashboardLoading || archivedDataChartLoading}
selectedId={selectedId}
onInit={recycleInit}
/>
</ListPane>
</Wrapper>
);
},
)
Example #13
Source File: index.tsx From datart with Apache License 2.0 | 4 votes |
Storyboards = memo(
({
selectedId,
className,
i18nPrefix,
sliderVisible,
handleSliderVisible,
}: FoldersProps) => {
const dispatch = useDispatch();
const orgId = useSelector(selectOrgId);
const { showSaveForm } = useContext(SaveFormContext);
const list = useSelector(selectStoryboards);
const allowCreate = useAccess(allowCreateStoryboard());
const t = useI18NPrefix(i18nPrefix);
const { filteredData: filteredListData, debouncedSearch: listSearch } =
useDebouncedSearch(list, (keywords, d) =>
d.name.toLowerCase().includes(keywords.toLowerCase()),
);
const archived = useSelector(selectArchivedStoryboards);
const archivedListLoading = useSelector(selectArchivedStoryboardLoading);
const {
filteredData: filteredRecycleData,
debouncedSearch: recycleSearch,
} = useDebouncedSearch(archived, (keywords, d) =>
d.name.toLowerCase().includes(keywords.toLowerCase()),
);
const recycleInit = useCallback(() => {
dispatch(getArchivedStoryboards(orgId));
}, [dispatch, orgId]);
const add = useCallback(() => {
showSaveForm({
vizType: 'STORYBOARD',
type: CommonFormTypes.Add,
visible: true,
onSave: (values, onClose) => {
dispatch(
addStoryboard({
storyboard: { name: values.name, orgId },
resolve: onClose,
}),
);
},
});
}, [showSaveForm, dispatch, orgId]);
const titles = useMemo(
() => [
{
subTitle: t('storyboards.title'),
search: true,
...allowCreate({
add: {
items: [{ key: 'STORYBOARD', text: t('storyboards.add') }],
icon: <PlusOutlined />,
callback: add,
},
}),
more: {
items: [
{
key: 'recycle',
text: t('storyboards.recycle'),
prefix: <DeleteOutlined className="icon" />,
},
{
key: 'collapse',
text: t(sliderVisible ? 'folders.open' : 'folders.close'),
prefix: sliderVisible ? (
<MenuUnfoldOutlined className="icon" />
) : (
<MenuFoldOutlined className="icon" />
),
},
],
callback: (key, _, onNext) => {
switch (key) {
case 'recycle':
onNext();
break;
case 'collapse':
handleSliderVisible(!sliderVisible);
break;
}
},
},
onSearch: listSearch,
},
{
key: 'recycle',
subTitle: t('storyboards.recycle'),
back: true,
search: true,
onSearch: recycleSearch,
},
],
[
add,
allowCreate,
listSearch,
recycleSearch,
t,
handleSliderVisible,
sliderVisible,
],
);
return (
<Wrapper className={className} defaultActiveKey="list">
<ListPane key="list">
<ListTitle {...titles[0]} />
<List list={filteredListData} selectedId={selectedId} />
</ListPane>
<ListPane key="recycle">
<ListTitle {...titles[1]} />
<Recycle
type="storyboard"
orgId={orgId}
list={filteredRecycleData}
listLoading={archivedListLoading}
selectedId={selectedId}
onInit={recycleInit}
/>
</ListPane>
</Wrapper>
);
},
)
Example #14
Source File: index.tsx From datart with Apache License 2.0 | 4 votes |
Sidebar = memo(
({
width,
isDragging,
i18nPrefix,
sliderVisible,
handleSliderVisible,
}: SidebarProps) => {
const [selectedKey, setSelectedKey] = useState('folder');
const vizs = useSelector(selectVizs);
const storyboards = useSelector(selectStoryboards);
const matchDetail = useRouteMatch<{ vizId: string }>(
'/organizations/:orgId/vizs/:vizId',
);
const vizId = matchDetail?.params.vizId;
const t = useI18NPrefix(i18nPrefix);
const selectedFolderId = useMemo(() => {
if (vizId && vizs) {
const viz = vizs.find(({ relId }) => relId === vizId);
return viz && viz.id;
}
}, [vizId, vizs]);
useEffect(() => {
if (vizId) {
const viz =
vizs.find(({ relId }) => relId === vizId) ||
storyboards.find(({ id }) => id === vizId);
if (viz) {
setSelectedKey((viz as Folder).relType ? 'folder' : 'presentation');
}
}
}, [vizId, storyboards, vizs]); // just switch when vizId changed
const listTitles = useMemo(
() => [
{ key: 'folder', icon: <FolderAddFilled />, text: t('folder') },
{
key: 'presentation',
icon: <FundProjectionScreenOutlined />,
text: t('presentation'),
},
],
[t],
);
const switchSelect = useCallback(key => {
setSelectedKey(key);
}, []);
return (
<Wrapper
sliderVisible={sliderVisible}
className={sliderVisible ? 'close' : ''}
isDragging={isDragging}
width={width}
>
{sliderVisible ? (
<MenuUnfoldOutlined className="menuUnfoldOutlined" />
) : (
''
)}
<ListSwitch
titles={listTitles}
selectedKey={selectedKey}
onSelect={switchSelect}
/>
<Folders
sliderVisible={sliderVisible}
handleSliderVisible={handleSliderVisible}
selectedId={selectedFolderId}
i18nPrefix={i18nPrefix}
className={classnames({ hidden: selectedKey !== 'folder' })}
/>
<Storyboards
sliderVisible={sliderVisible}
handleSliderVisible={handleSliderVisible}
selectedId={vizId}
className={classnames({ hidden: selectedKey !== 'presentation' })}
i18nPrefix={i18nPrefix}
/>
</Wrapper>
);
},
)
Example #15
Source File: MainOperator.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
Main: React.FC<MainProp> = (props) => {
const [engineStatus, setEngineStatus] = useState<"ok" | "error">("ok")
const [status, setStatus] = useState<{ addr: string; isTLS: boolean }>()
const [collapsed, setCollapsed] = useState(false)
const [hideMenu, setHideMenu] = useState(false)
const [loading, setLoading] = useState(false)
const [menuItems, setMenuItems] = useState<MenuItemGroup[]>([])
const [routeMenuData, setRouteMenuData] = useState<MenuDataProps[]>(RouteMenuData)
const [notification, setNotification] = useState("")
const [pageCache, setPageCache] = useState<PageCache[]>([
{
verbose: "MITM",
route: Route.HTTPHacker,
singleNode: ContentByRoute(Route.HTTPHacker),
multipleNode: []
}
])
const [currentTabKey, setCurrentTabKey] = useState<string>(Route.HTTPHacker)
// 系统类型
const [system, setSystem] = useState<string>("")
useEffect(() => {
ipcRenderer.invoke('fetch-system-name').then((res) => setSystem(res))
}, [])
// yakit页面关闭是否二次确认提示
const [winCloseFlag, setWinCloseFlag] = useState<boolean>(true)
const [winCloseShow, setWinCloseShow] = useState<boolean>(false)
useEffect(() => {
ipcRenderer
.invoke("get-value", WindowsCloseFlag)
.then((flag: any) => setWinCloseFlag(flag === undefined ? true : flag))
}, [])
// 获取自定义菜单
const updateMenuItems = () => {
setLoading(true)
// Fetch User Defined Plugins
ipcRenderer
.invoke("GetAllMenuItem", {})
.then((data: { Groups: MenuItemGroup[] }) => {
setMenuItems(data.Groups)
})
.catch((e: any) => failed("Update Menu Item Failed"))
.finally(() => setTimeout(() => setLoading(false), 300))
// Fetch Official General Plugins
ipcRenderer
.invoke("QueryYakScript", {
Pagination: genDefaultPagination(1000),
IsGeneralModule: true,
Type: "yak"
} as QueryYakScriptRequest)
.then((data: QueryYakScriptsResponse) => {
const tabList: MenuDataProps[] = cloneDeep(RouteMenuData)
for (let item of tabList) {
if (item.subMenuData) {
if (item.key === Route.GeneralModule) {
const extraMenus: MenuDataProps[] = data.Data.map((i) => {
return {
icon: <EllipsisOutlined/>,
key: `plugin:${i.Id}`,
label: i.ScriptName,
} as unknown as MenuDataProps
})
item.subMenuData.push(...extraMenus)
}
item.subMenuData.sort((a, b) => a.label.localeCompare(b.label))
}
}
setRouteMenuData(tabList)
})
}
useEffect(() => {
updateMenuItems()
ipcRenderer.on("fetch-new-main-menu", (e) => {
updateMenuItems()
})
return () => {
ipcRenderer.removeAllListeners("fetch-new-main-menu")
}
}, [])
useEffect(() => {
if (engineStatus === "error") props.onErrorConfirmed && props.onErrorConfirmed()
}, [engineStatus])
// 整合路由对应名称
const pluginKey = (item: PluginMenuItem) => `plugin:${item.Group}:${item.YakScriptId}`;
const routeKeyToLabel = new Map<string, string>();
routeMenuData.forEach(k => {
(k.subMenuData || []).forEach(subKey => {
routeKeyToLabel.set(`${subKey.key}`, subKey.label)
})
routeKeyToLabel.set(`${k.key}`, k.label)
})
menuItems.forEach((k) => {
k.Items.forEach((value) => {
routeKeyToLabel.set(pluginKey(value), value.Verbose)
})
})
// Tabs Bar Operation Function
const getCacheIndex = (route: string) => {
const targets = pageCache.filter((i) => i.route === route)
return targets.length > 0 ? pageCache.indexOf(targets[0]) : -1
}
const addTabPage = useMemoizedFn(
(route: Route, nodeParams?: { time?: string; node: ReactNode; isRecord?: boolean }) => {
const filterPage = pageCache.filter((i) => i.route === route)
const filterPageLength = filterPage.length
if (singletonRoute.includes(route)) {
if (filterPageLength > 0) {
setCurrentTabKey(route)
} else {
const tabName = routeKeyToLabel.get(route) || `${route}`
setPageCache([
...pageCache,
{
verbose: tabName,
route: route,
singleNode: ContentByRoute(route),
multipleNode: []
}
])
setCurrentTabKey(route)
}
} else {
if (filterPageLength > 0) {
const tabName = routeKeyToLabel.get(route) || `${route}`
const tabId = `${route}-[${randomString(49)}]`
const time = new Date().getTime().toString()
const node: multipleNodeInfo = {
id: tabId,
verbose: `${tabName}-[${filterPage[0].multipleNode.length + 1}]`,
node: nodeParams && nodeParams.node ? nodeParams?.node || <></> : ContentByRoute(route),
time: nodeParams && nodeParams.node ? nodeParams?.time || time : time
}
const pages = pageCache.map((item) => {
if (item.route === route) {
item.multipleNode.push(node)
item.multipleCurrentKey = tabId
return item
}
return item
})
setPageCache([...pages])
setCurrentTabKey(route)
if (nodeParams && !!nodeParams.isRecord) addFuzzerList(nodeParams?.time || time)
} else {
const tabName = routeKeyToLabel.get(route) || `${route}`
const tabId = `${route}-[${randomString(49)}]`
const time = new Date().getTime().toString()
const node: multipleNodeInfo = {
id: tabId,
verbose: `${tabName}-[1]`,
node: nodeParams && nodeParams.node ? nodeParams?.node || <></> : ContentByRoute(route),
time: nodeParams && nodeParams.node ? nodeParams?.time || time : time
}
setPageCache([
...pageCache,
{
verbose: tabName,
route: route,
singleNode: undefined,
multipleNode: [node],
multipleCurrentKey: tabId
}
])
setCurrentTabKey(route)
if (nodeParams && !!nodeParams.isRecord) addFuzzerList(nodeParams?.time || time)
}
}
}
)
const menuAddPage = useMemoizedFn((route: Route) => {
if (route === "ignore") return
if (route === Route.HTTPFuzzer) {
const time = new Date().getTime().toString()
addTabPage(Route.HTTPFuzzer, {
time: time,
node: ContentByRoute(Route.HTTPFuzzer, undefined, {
system: system,
order: time
}),
isRecord: true
})
} else addTabPage(route as Route)
})
const removePage = (route: string) => {
const targetIndex = getCacheIndex(route)
if (targetIndex > 0 && pageCache[targetIndex - 1]) {
const targetCache = pageCache[targetIndex - 1]
setCurrentTabKey(targetCache.route)
}
if (targetIndex === 0 && pageCache[targetIndex + 1]) {
const targetCache = pageCache[targetIndex + 1]
setCurrentTabKey(targetCache.route)
}
if (targetIndex === 0 && pageCache.length === 1) setCurrentTabKey("")
setPageCache(pageCache.filter((i) => i.route !== route))
if (route === Route.HTTPFuzzer) delFuzzerList(1)
}
const updateCacheVerbose = (id: string, verbose: string) => {
const index = getCacheIndex(id)
if (index < 0) return
pageCache[index].verbose = verbose
setPageCache([...pageCache])
}
const setMultipleCurrentKey = useMemoizedFn((key: string, type: Route) => {
const arr = pageCache.map(item => {
if (item.route === type) {
item.multipleCurrentKey = key
return item
}
return item
})
setPageCache([...arr])
})
const removeMultipleNodePage = useMemoizedFn((key: string, type: Route) => {
const removeArr: multipleNodeInfo[] = pageCache.filter(item => item.route === type)[0]?.multipleNode || []
if (removeArr.length === 0) return
const nodes = removeArr.filter(item => item.id === key)
const time = nodes[0].time
let index = 0
for (let i in removeArr) {
if (removeArr[i].id === key) {
index = +i
break
}
}
if (index === 0 && removeArr.length === 1) {
removePage(type)
return
}
let current = ""
let filterArr: multipleNodeInfo[] = []
if (index > 0 && removeArr[index - 1]) {
current = removeArr[index - 1].id
filterArr = removeArr.filter(item => item.id !== key)
}
if (index === 0 && removeArr[index + 1]) {
current = removeArr[index + 1].id
filterArr = removeArr.filter(item => item.id !== key)
}
if (current) {
const arr = pageCache.map(item => {
if (item.route === type) {
item.multipleNode = [...filterArr]
item.multipleCurrentKey = current
return item
}
return item
})
setPageCache([...arr])
if (type === Route.HTTPFuzzer) delFuzzerList(2, time)
}
})
const removeOtherMultipleNodePage = useMemoizedFn((key: string, type: Route) => {
const removeArr: multipleNodeInfo[] = pageCache.filter(item => item.route === type)[0]?.multipleNode || []
if (removeArr.length === 0) return
const nodes = removeArr.filter(item => item.id === key)
const time = nodes[0].time
const arr = pageCache.map(item => {
if (item.route === type) {
item.multipleNode = [...nodes]
item.multipleCurrentKey = key
return item
}
return item
})
setPageCache([...arr])
if (type === Route.HTTPFuzzer) delFuzzerList(3, time)
})
// 全局记录鼠标坐标位置(为右键菜单提供定位)
const coordinateTimer = useRef<any>(null)
useEffect(() => {
document.onmousemove = (e) => {
const {screenX, screenY, clientX, clientY, pageX, pageY} = e
if (coordinateTimer.current) {
clearTimeout(coordinateTimer.current)
coordinateTimer.current = null
}
coordinateTimer.current = setTimeout(() => {
coordinate.screenX = screenX
coordinate.screenY = screenY
coordinate.clientX = clientX
coordinate.clientY = clientY
coordinate.pageX = pageX
coordinate.pageY = pageY
}, 50);
}
}, [])
// 全局注册快捷键功能
const documentKeyDown = useMemoizedFn((e: any) => {
// ctrl + w 关闭tab页面
if (e.code === "KeyW" && (e.ctrlKey || e.metaKey)) {
e.preventDefault()
if (pageCache.length === 0) return
setLoading(true)
removePage(currentTabKey)
setTimeout(() => setLoading(false), 300);
return
}
})
useEffect(() => {
document.onkeydown = documentKeyDown
}, [])
// fuzzer本地缓存
const fuzzerList = useRef<Map<string, fuzzerInfoProp>>(new Map<string, fuzzerInfoProp>())
const saveFuzzerList = debounce(() => {
const historys: fuzzerInfoProp[] = []
fuzzerList.current.forEach((value) => historys.push(value))
historys.sort((a, b) => +a.time - +b.time)
const filters = historys.filter(item => (item.request || "").length < 1000000 && (item.request || "").length > 0)
ipcRenderer.invoke("set-value", FuzzerCache, JSON.stringify(filters.slice(-5)))
}, 500)
const fetchFuzzerList = useMemoizedFn(() => {
setLoading(true)
fuzzerList.current.clear()
ipcRenderer
.invoke("get-value", FuzzerCache)
.then((res: any) => {
const cache = JSON.parse(res || "[]")
for (let item of cache) {
const time = new Date().getTime().toString()
fuzzerList.current.set(time, {...item, time: time})
addTabPage(Route.HTTPFuzzer, {
time: time,
node:
ContentByRoute(
Route.HTTPFuzzer,
undefined,
{
isHttps: item.isHttps || false,
request: item.request || "",
fuzzerParams: item,
system: system,
order: time
}
)
})
}
})
.catch((e) => console.info(e))
.finally(() => setTimeout(() => setLoading(false), 300))
})
const addFuzzerList = (key: string, request?: string, isHttps?: boolean) => {
fuzzerList.current.set(key, {request, isHttps, time: key})
}
const delFuzzerList = (type: number, key?: string) => {
if (type === 1) fuzzerList.current.clear()
if (type === 2 && key) if (fuzzerList.current.has(key)) fuzzerList.current.delete(key)
if (type === 3 && key) {
const info = fuzzerList.current.get(key)
if (info) {
fuzzerList.current.clear()
fuzzerList.current.set(key, info)
}
}
saveFuzzerList()
}
const updateFuzzerList = (key: string, param: fuzzerInfoProp) => {
fuzzerList.current.set(key, param)
saveFuzzerList()
}
useEffect(() => {
ipcRenderer.on("fetch-fuzzer-setting-data", (e, res: any) => updateFuzzerList(res.key, JSON.parse(res.param)))
// 开发环境不展示fuzzer缓存
ipcRenderer.invoke("is-dev").then((flag) => {
if (!flag) fetchFuzzerList()
// fetchFuzzerList()
})
return () => ipcRenderer.removeAllListeners("fetch-fuzzer-setting-data")
}, [])
// 加载补全
useEffect(() => {
ipcRenderer.invoke("GetYakitCompletionRaw").then((data: { RawJson: Uint8Array }) => {
const completionJson = Buffer.from(data.RawJson).toString("utf8")
setCompletions(JSON.parse(completionJson) as CompletionTotal)
// success("加载 Yak 语言自动补全成功 / Load Yak IDE Auto Completion Finished")
})
}, [])
useEffect(() => {
ipcRenderer.invoke("yakit-connect-status").then((data) => {
setStatus(data)
})
ipcRenderer.on("client-engine-status-ok", (e, reason) => {
if (engineStatus !== "ok") setEngineStatus("ok")
})
ipcRenderer.on("client-engine-status-error", (e, reason) => {
if (engineStatus === "ok") setEngineStatus("error")
})
const updateEngineStatus = () => {
ipcRenderer
.invoke("engine-status")
.catch((e: any) => {
setEngineStatus("error")
})
.finally(() => {
})
}
let id = setInterval(updateEngineStatus, 3000)
return () => {
ipcRenderer.removeAllListeners("client-engine-status-error")
ipcRenderer.removeAllListeners("client-engine-status-ok")
clearInterval(id)
}
}, [])
useHotkeys("Ctrl+Alt+T", () => {
})
useEffect(() => {
ipcRenderer.invoke("query-latest-notification").then((e: string) => {
setNotification(e)
if (e) {
success(
<>
<Space direction={"vertical"}>
<span>来自于 yaklang.io 的通知</span>
<Button
type={"link"}
onClick={() => {
showModal({
title: "Notification",
content: (
<>
<MDEditor.Markdown source={e}/>
</>
)
})
}}
>
点击查看
</Button>
</Space>
</>
)
}
})
}, [])
// 新增数据对比页面
useEffect(() => {
ipcRenderer.on("main-container-add-compare", (e, params) => {
const newTabId = `${Route.DataCompare}-[${randomString(49)}]`;
const verboseNameRaw = routeKeyToLabel.get(Route.DataCompare) || `${Route.DataCompare}`;
addTabPage(Route.DataCompare, {node: ContentByRoute(Route.DataCompare, undefined, {system: system})})
// 区分新建对比页面还是别的页面请求对比的情况
ipcRenderer.invoke("created-data-compare")
})
return () => {
ipcRenderer.removeAllListeners("main-container-add-compare")
}
}, [pageCache])
// Global Sending Function(全局发送功能|通过发送新增功能页面)
const addFuzzer = useMemoizedFn((res: any) => {
const {isHttps, request} = res || {}
if (request) {
const time = new Date().getTime().toString()
addTabPage(Route.HTTPFuzzer, {
time: time,
node:
ContentByRoute(
Route.HTTPFuzzer,
undefined,
{
isHttps: isHttps || false,
request: request || "",
system: system,
order: time
}
)
})
addFuzzerList(time, request || "", isHttps || false)
}
})
const addScanPort = useMemoizedFn((res: any) => {
const {URL = ""} = res || {}
if (URL) {
addTabPage(Route.Mod_ScanPort, {
node: ContentByRoute(Route.Mod_ScanPort, undefined, {scanportParams: URL})
})
}
})
const addBrute = useMemoizedFn((res: any) => {
const {URL = ""} = res || {}
if (URL) {
addTabPage(Route.Mod_Brute, {
node: ContentByRoute(Route.Mod_Brute, undefined, {bruteParams: URL})
})
}
})
// 发送到专项漏洞检测modal-show变量
const [bugTestShow, setBugTestShow] = useState<boolean>(false)
const [bugList, setBugList] = useState<BugInfoProps[]>([])
const [bugTestValue, setBugTestValue] = useState<BugInfoProps[]>([])
const [bugUrl, setBugUrl] = useState<string>("")
const addBugTest = useMemoizedFn((type: number, res?: any) => {
const {URL = ""} = res || {}
if (type === 1 && URL) {
setBugUrl(URL)
ipcRenderer.invoke("get-value", CustomBugList)
.then((res: any) => {
setBugList(res ? JSON.parse(res) : [])
setBugTestShow(true)
})
.catch(() => {
})
}
if (type === 2) {
const filter = pageCache.filter(item => item.route === Route.PoC)
if (filter.length === 0) {
addTabPage(Route.PoC)
setTimeout(() => {
ipcRenderer.invoke("send-to-bug-test", {type: bugTestValue, data: bugUrl})
setBugTestValue([])
setBugUrl("")
}, 300);
} else {
ipcRenderer.invoke("send-to-bug-test", {type: bugTestValue, data: bugUrl})
setCurrentTabKey(Route.PoC)
setBugTestValue([])
setBugUrl("")
}
}
})
const addYakRunning = useMemoizedFn((res: any) => {
const {name = "", code = ""} = res || {}
const filter = pageCache.filter(item => item.route === Route.YakScript)
if (!name || !code) return false
if ((filter || []).length === 0) {
addTabPage(Route.YakScript)
setTimeout(() => {
ipcRenderer.invoke("send-to-yak-running", {name, code})
}, 300);
} else {
ipcRenderer.invoke("send-to-yak-running", {name, code})
setCurrentTabKey(Route.YakScript)
}
})
useEffect(() => {
ipcRenderer.on("fetch-send-to-tab", (e, res: any) => {
const {type, data = {}} = res
if (type === "fuzzer") addFuzzer(data)
if (type === "scan-port") addScanPort(data)
if (type === "brute") addBrute(data)
if (type === "bug-test") addBugTest(1, data)
if (type === "plugin-store") addYakRunning(data)
})
return () => {
ipcRenderer.removeAllListeners("fetch-send-to-tab")
}
}, [])
// Tabs Bar 组件
const closeAllCache = useMemoizedFn(() => {
Modal.confirm({
title: "确定要关闭所有 Tabs?",
content: "这样将会关闭所有进行中的进程",
onOk: () => {
delFuzzerList(1)
setPageCache([])
}
})
})
const closeOtherCache = useMemoizedFn((route: string) => {
Modal.confirm({
title: "确定要关闭除此之外所有 Tabs?",
content: "这样将会关闭所有进行中的进程",
onOk: () => {
const arr = pageCache.filter((i) => i.route === route)
setPageCache(arr)
if (route === Route.HTTPFuzzer) delFuzzerList(1)
}
})
})
const bars = (props: any, TabBarDefault: any) => {
return (
<TabBarDefault
{...props}
children={(barNode: React.ReactElement) => {
return (
<DropdownMenu
menu={{
data: [
{key: "all", title: "关闭所有Tabs"},
{key: "other", title: "关闭其他Tabs"}
]
}}
dropdown={{trigger: ["contextMenu"]}}
onClick={(key) => {
switch (key) {
case "all":
closeAllCache()
break
case "other":
closeOtherCache(barNode.key as Route)
break
default:
break
}
}}
>
{barNode}
</DropdownMenu>
)
}}
/>
)
}
return (
<Layout className="yakit-main-layout">
<AutoSpin spinning={loading}>
<Header className="main-laytou-header">
<Row>
<Col span={8}>
<Space>
<div style={{marginLeft: 18, textAlign: "center", height: 60}}>
<Image src={YakLogoBanner} preview={false} width={130} style={{marginTop: 6}}/>
</div>
<Divider type={"vertical"}/>
<YakVersion/>
<YakitVersion/>
{!hideMenu && (
<Button
style={{marginLeft: 4, color: "#207ee8"}}
type={"ghost"}
ghost={true}
onClick={(e) => {
setCollapsed(!collapsed)
}}
icon={collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
/>
)}
<Button
style={{marginLeft: 4, color: "#207ee8"}}
type={"ghost"}
ghost={true}
onClick={(e) => {
updateMenuItems()
}}
icon={<ReloadOutlined/>}
/>
</Space>
</Col>
<Col span={16} style={{textAlign: "right", paddingRight: 28}}>
<PerformanceDisplay/>
<RiskStatsTag professionalMode={true}/>
<Space>
{/* {status?.isTLS ? <Tag color={"green"}>TLS:通信已加密</Tag> : <Tag color={"red"}>通信未加密</Tag>} */}
{status?.addr && <Tag color={"geekblue"}>{status?.addr}</Tag>}
{/* <Tag color={engineStatus === "ok" ? "green" : "red"}>Yak 引擎状态:{engineStatus}</Tag> */}
<ReversePlatformStatus/>
<Dropdown forceRender={true} overlay={<Menu>
<Menu.Item key={"update"}>
<AutoUpdateYakModuleButton/>
</Menu.Item>
<Menu.Item key={"reverse-global"}>
<ConfigGlobalReverseButton/>
</Menu.Item>
</Menu>} trigger={["click"]}>
<Button icon={<SettingOutlined/>}>
配置
</Button>
</Dropdown>
<Button type={"link"} danger={true} icon={<PoweroffOutlined/>} onClick={() => {
if (winCloseFlag) setWinCloseShow(true)
else {
success("退出当前 Yak 服务器成功")
setEngineStatus("error")
}
}}/>
</Space>
</Col>
</Row>
</Header>
<Content
style={{
margin: 12,
backgroundColor: "#fff",
overflow: "auto"
}}
>
<Layout style={{height: "100%", overflow: "hidden"}}>
{!hideMenu && (
<Sider
style={{backgroundColor: "#fff", overflow: "auto"}}
collapsed={collapsed}
>
<Spin spinning={loading}>
<Space
direction={"vertical"}
style={{
width: "100%"
}}
>
<Menu
theme={"light"}
style={{}}
selectedKeys={[]}
mode={"inline"}
onSelect={(e) => {
if (e.key === "ignore") return
const flag = pageCache.filter(item => item.route === (e.key as Route)).length === 0
if (flag) menuAddPage(e.key as Route)
else setCurrentTabKey(e.key)
}}
>
{menuItems.map((i) => {
if (i.Group === "UserDefined") {
i.Group = "社区插件"
}
return (
<Menu.SubMenu icon={<EllipsisOutlined/>} key={i.Group}
title={i.Group}>
{i.Items.map((item) => {
if (item.YakScriptId > 0) {
return (
<MenuItem icon={<EllipsisOutlined/>}
key={`plugin:${item.Group}:${item.YakScriptId}`}>
<Text
ellipsis={{tooltip: true}}>{item.Verbose}</Text>
</MenuItem>
)
}
return (
<MenuItem icon={<EllipsisOutlined/>}
key={`batch:${item.Group}:${item.Verbose}:${item.MenuItemId}`}>
<Text
ellipsis={{tooltip: true}}>{item.Verbose}</Text>
</MenuItem>
)
})}
</Menu.SubMenu>
)
})}
{(routeMenuData || []).map((i) => {
if (i.subMenuData) {
return (
<Menu.SubMenu icon={i.icon} key={i.key} title={i.label}>
{(i.subMenuData || []).map((subMenu) => {
return (
<MenuItem icon={subMenu.icon} key={subMenu.key}
disabled={subMenu.disabled}>
<Text
ellipsis={{tooltip: true}}>{subMenu.label}</Text>
</MenuItem>
)
})}
</Menu.SubMenu>
)
}
return (
<MenuItem icon={i.icon} key={i.key} disabled={i.disabled}>
{i.label}
</MenuItem>
)
})}
</Menu>
</Space>
</Spin>
</Sider>
)}
<Content style={{
overflow: "hidden",
backgroundColor: "#fff",
marginLeft: 12,
height: "100%",
display: "flex",
flexFlow: "column"
}}>
<div style={{
padding: 12,
paddingTop: 8,
overflow: "hidden",
flex: "1",
display: "flex",
flexFlow: "column"
}}>
{pageCache.length > 0 ? (
<Tabs
style={{display: "flex", flex: "1"}}
tabBarStyle={{marginBottom: 8}}
className='main-content-tabs yakit-layout-tabs'
activeKey={currentTabKey}
onChange={setCurrentTabKey}
size={"small"}
type={"editable-card"}
renderTabBar={(props, TabBarDefault) => {
return bars(props, TabBarDefault)
}}
hideAdd={true}
onTabClick={(key, e) => {
const divExisted = document.getElementById("yakit-cursor-menu")
if (divExisted) {
const div: HTMLDivElement = divExisted as HTMLDivElement
const unmountResult = ReactDOM.unmountComponentAtNode(div)
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div)
}
}
}}
>
{pageCache.map((i) => {
return (
<Tabs.TabPane
forceRender={true}
key={i.route}
tab={i.verbose}
closeIcon={
<Space>
<Popover
trigger={"click"}
title={"修改名称"}
content={
<>
<Input
size={"small"}
defaultValue={i.verbose}
onBlur={(e) => updateCacheVerbose(i.route, e.target.value)}
/>
</>
}
>
<EditOutlined className='main-container-cion'/>
</Popover>
<CloseOutlined
className='main-container-cion'
onClick={() => removePage(i.route)}
/>
</Space>
}
>
<div
style={{
overflowY: NoScrollRoutes.includes(i.route)
? "hidden"
: "auto",
overflowX: "hidden",
height: "100%",
maxHeight: "100%"
}}
>
{i.singleNode ? (
i.singleNode
) : (
<MainTabs
currentTabKey={currentTabKey}
tabType={i.route}
pages={i.multipleNode}
currentKey={i.multipleCurrentKey || ""}
isShowAdd={true}
setCurrentKey={(key, type) => {
setMultipleCurrentKey(key, type as Route)
}}
removePage={(key, type) => {
removeMultipleNodePage(key, type as Route)
}}
removeOtherPage={(key, type) => {
removeOtherMultipleNodePage(key, type as Route)
}}
onAddTab={() => menuAddPage(i.route)}
></MainTabs>
)}
</div>
</Tabs.TabPane>
)
})}
</Tabs>
) : (
<></>
)}
</div>
</Content>
</Layout>
</Content>
</AutoSpin>
<Modal
visible={winCloseShow}
onCancel={() => setWinCloseShow(false)}
footer={[
<Button key='link' onClick={() => setWinCloseShow(false)}>
取消
</Button>,
<Button key='back' type='primary' onClick={() => {
success("退出当前 Yak 服务器成功")
setEngineStatus("error")
}}>
退出
</Button>
]}
>
<div style={{height: 40}}>
<ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}}/>
<span style={{fontSize: 18, marginLeft: 15}}>提示</span>
</div>
<p style={{fontSize: 15, marginLeft: 37}}>是否要退出yakit操作界面,一旦退出,界面内打开内容除fuzzer页外都会销毁</p>
<div style={{marginLeft: 37}}>
<Checkbox
defaultChecked={!winCloseFlag}
value={!winCloseFlag}
onChange={() => {
setWinCloseFlag(!winCloseFlag)
ipcRenderer.invoke("set-value", WindowsCloseFlag, false)
}}
></Checkbox>
<span style={{marginLeft: 8}}>不再出现该提示信息</span>
</div>
</Modal>
<Modal
visible={bugTestShow}
onCancel={() => setBugTestShow(false)}
footer={[
<Button key='link' onClick={() => setBugTestShow(false)}>
取消
</Button>,
<Button key='back' type='primary' onClick={() => {
if ((bugTestValue || []).length === 0) return failed("请选择类型后再次提交")
addBugTest(2)
setBugTestShow(false)
}}>
确定
</Button>
]}
>
<ItemSelects
item={{
label: "专项漏洞类型",
style: {marginTop: 20}
}}
select={{
allowClear: true,
data: BugList.concat(bugList) || [],
optText: "title",
optValue: "key",
value: (bugTestValue || [])[0]?.key,
onChange: (value, option: any) => setBugTestValue(value ? [{
key: option?.key,
title: option?.title
}] : [])
}}
></ItemSelects>
</Modal>
</Layout>
)
}