@ant-design/icons#FullscreenOutlined TypeScript Examples
The following examples show how to use
@ant-design/icons#FullscreenOutlined.
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 covid_dashboard with MIT License | 6 votes |
render() {
return <div className='search-box'>
{this.state.items.length > 0 && <div className="search-result" style={{width: this.state.size === 'small' ? 300 : 450}}>
<div className="search-control">
<div className="control-btn" onClick={() => this.setState({size: this.state.size === 'small' ? 'large' : 'small'})}>{this.state.size === 'small' ? <FullscreenOutlined/> : <FullscreenExitOutlined/>}</div>
<div className="control-btn" onClick={() => this.setState({items: []})}><CloseCircleOutlined/></div>
</div>
<div className="search-items" style={{maxHeight: this.state.size === 'small' ? 350 : 500}}>
{this.state.items.map(item => {
return <div key={item._id} className="search-item" onClick={() => this.onClickEvent(item)}>
<div className="event-type" style={{background: getEventColor(item.type)}}>{_.capitalize(item.type)}</div>
<div className="event-time">{item.time}</div>
<div className="event-title">{item.title}</div>
</div>
})}
</div>
</div>}
<div className="search-input">
<Input.Search placeholder={this.props.intl.formatMessage({id: "search.placeholder"})} onChange={(e) => this.search(e.target.value)} onSearch={(text) => this.search(text)} loading={this.state.loading}/><span className='close' onClick={() => this.props.onClose && this.props.onClose()}><CloseCircleOutlined/></span>
</div>
</div>
}
Example #2
Source File: BuildingPanelCommon.tsx From condo with MIT License | 6 votes |
BuildingChooseSections: React.FC<IBuildingChooseSectionsProps> = (props) => {
const intl = useIntl()
const RequestFullscreenMessage = intl.formatMessage({ id: 'FullscreenRequest' })
const ExitFullscreenMessage = intl.formatMessage({ id: 'FullscreenExit' })
const {
toggleFullscreen,
isFullscreen,
mode = 'view',
children,
} = props
return (
<Row
css={FullscreenFooter}
gutter={FULLSCREEN_FOOTER_GUTTER}
>
<Col>
{mode === 'view' ? (
<Button
style={FULLSCREEN_BUTTON_STYLE}
type={'sberDefaultGradient'}
secondary
icon={isFullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
size={'large'}
onClick={toggleFullscreen}
>
{isFullscreen
? ExitFullscreenMessage
: RequestFullscreenMessage}
</Button>
) : children}
</Col>
</Row>
)
}
Example #3
Source File: Tile.tsx From ant-extensions with MIT License | 5 votes |
Tile: React.FC<ITileConfig> = React.memo((item) => {
const { isEditing, editWidget, renderWidget, findWidget } = useContext(Context);
const style = useMemo(
() => ({
color: item.color || "inherit"
}),
[item.color]
);
const [expanded, setExpanded] = useState(false);
const widget = useMemo(() => findWidget(item.widgetId), [findWidget, item.widgetId]);
return (
<Item item={item} expanded={!isEditing && expanded}>
<div className="ant-ext-pm__tileHead">
<span style={style}>{item.iconCls && <i className={item.iconCls} />}</span>
<label style={style}>{item.title}</label>
<div>
{item.info && (
<Tooltip
overlay={<pre dangerouslySetInnerHTML={{ __html: item.info }} />}
overlayClassName="ant-ext-pm__tileInfo"
>
<button>
<InfoCircleOutlined />
</button>
</Tooltip>
)}
{!isEditing && item.expandable && (
<button className="ant-ext-pm__tileExpander" onClick={() => setExpanded(!expanded)}>
{expanded ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
</button>
)}
{isEditing && (
<button onClick={() => editWidget(item.widgetId)}>
<EditOutlined />
</button>
)}
</div>
</div>
<div className="ant-ext-pm__tileBody">
{!isEditing && renderWidget(item.widgetId)}
{isEditing && widget && (
<div style={{ placeSelf: "center", textAlign: "center" }}>
{widget.icon}
<div>{widget.title}</div>
</div>
)}
</div>
</Item>
);
})
Example #4
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 #5
Source File: DashboardHeader.tsx From posthog-foss with MIT License | 4 votes |
export function DashboardHeader(): JSX.Element {
const { dashboard, dashboardMode, lastDashboardModeSource } = useValues(dashboardLogic)
const { addNewDashboard, triggerDashboardUpdate, setDashboardMode, addGraph, saveNewTag, deleteTag } =
useActions(dashboardLogic)
const { dashboardTags } = useValues(dashboardsLogic)
const { nameSortedDashboards, dashboardsLoading, dashboardLoading } = useValues(dashboardsModel)
const { pinDashboard, unpinDashboard, deleteDashboard, duplicateDashboard } = useActions(dashboardsModel)
const { user } = useValues(userLogic)
const [newName, setNewName] = useState(dashboard?.name || null) // Used to update the input immediately, debouncing API calls
const nameInputRef = useRef<Input | null>(null)
const descriptionInputRef = useRef<HTMLInputElement | null>(null)
if (!dashboard) {
return <div />
}
const actionsDefault = (
<>
<Dropdown
trigger={['click']}
overlay={
<Menu>
{dashboard.created_by && (
<>
<Menu.Item disabled>
Created by {dashboard.created_by.first_name || dashboard.created_by.email || '-'} on{' '}
{dayjs(dashboard.created_at).format(
dayjs(dashboard.created_at).year() === dayjs().year()
? 'MMMM Do'
: 'MMMM Do YYYY'
)}
</Menu.Item>
<Menu.Divider />
</>
)}
<Menu.Item
icon={<EditOutlined />}
onClick={() => setDashboardMode(DashboardMode.Edit, DashboardEventSource.MoreDropdown)}
>
Edit mode (E)
</Menu.Item>
<Menu.Item
icon={<FullscreenOutlined />}
onClick={() =>
setDashboardMode(DashboardMode.Fullscreen, DashboardEventSource.MoreDropdown)
}
>
Full screen mode (F)
</Menu.Item>
{dashboard.pinned ? (
<Menu.Item
icon={<PushpinFilled />}
onClick={() => unpinDashboard(dashboard.id, DashboardEventSource.MoreDropdown)}
>
Unpin dashboard
</Menu.Item>
) : (
<Menu.Item
icon={<PushpinOutlined />}
onClick={() => pinDashboard(dashboard.id, DashboardEventSource.MoreDropdown)}
>
Pin dashboard
</Menu.Item>
)}
<Menu.Divider />
<Menu.Item
icon={<CopyOutlined />}
onClick={() => duplicateDashboard({ id: dashboard.id, name: dashboard.name, show: true })}
>
Duplicate dashboard
</Menu.Item>
<Menu.Item
icon={<DeleteOutlined />}
onClick={() => deleteDashboard({ id: dashboard.id, redirect: true })}
danger
>
Delete dashboard
</Menu.Item>
</Menu>
}
placement="bottomRight"
>
<Button type="link" className="btn-lg-2x" data-attr="dashboard-more" icon={<EllipsisOutlined />} />
</Dropdown>
<Button
type="link"
data-attr="dashboard-edit-mode"
icon={<EditOutlined />}
onClick={() => setDashboardMode(DashboardMode.Edit, DashboardEventSource.DashboardHeader)}
/>
<HotkeyButton
onClick={() => addGraph()}
data-attr="dashboard-add-graph-header"
icon={<PlusOutlined />}
hotkey="n"
className="hide-lte-md"
>
New insight
</HotkeyButton>
<HotkeyButton
type="primary"
onClick={() => setDashboardMode(DashboardMode.Sharing, DashboardEventSource.DashboardHeader)}
data-attr="dashboard-share-button"
icon={<ShareAltOutlined />}
hotkey="k"
>
Send or share
</HotkeyButton>
</>
)
const actionsPresentationMode = (
<Button
onClick={() => setDashboardMode(null, DashboardEventSource.DashboardHeader)}
data-attr="dashboard-exit-presentation-mode"
icon={<FullscreenExitOutlined />}
>
Exit full screen mode
</Button>
)
const actionsEditMode = (
<Button
data-attr="dashboard-edit-mode-save"
type="primary"
onClick={() => setDashboardMode(null, DashboardEventSource.DashboardHeader)}
tabIndex={10}
>
Finish editing
</Button>
)
useEffect(() => {
if (dashboardMode === DashboardMode.Edit) {
if (lastDashboardModeSource === DashboardEventSource.AddDescription) {
setTimeout(() => descriptionInputRef.current?.focus(), 10)
} else if (!isMobile()) {
setTimeout(() => nameInputRef.current?.focus(), 10)
}
}
}, [dashboardMode])
return (
<>
<div className={`dashboard-header${dashboardMode === DashboardMode.Fullscreen ? ' full-screen' : ''}`}>
{dashboardMode === DashboardMode.Fullscreen && (
<FullScreen onExit={() => setDashboardMode(null, DashboardEventSource.Browser)} />
)}
<ShareModal
onCancel={() => setDashboardMode(null, DashboardEventSource.Browser)}
visible={dashboardMode === DashboardMode.Sharing}
/>
{dashboardsLoading ? (
<Loading />
) : (
<>
{dashboardMode === DashboardMode.Edit ? (
<Input
placeholder="Dashboard name (e.g. Weekly KPIs)"
value={newName || ''}
size="large"
style={{ maxWidth: 400 }}
onChange={(e) => {
setNewName(e.target.value) // To update the input immediately
triggerDashboardUpdate({ name: e.target.value }) // This is breakpointed (i.e. debounced) to avoid multiple API calls
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
setDashboardMode(null, DashboardEventSource.InputEnter)
}
}}
ref={nameInputRef}
tabIndex={0}
/>
) : (
<div className="dashboard-select">
<Select
value={(dashboard?.id || undefined) as number | 'new' | undefined}
onChange={(id) => {
if (id === 'new') {
addNewDashboard()
} else {
router.actions.push(urls.dashboard(id))
eventUsageLogic.actions.reportDashboardDropdownNavigation()
}
}}
bordered={false}
dropdownMatchSelectWidth={false}
>
{nameSortedDashboards.map((dash: DashboardType) => (
<Select.Option key={dash.id} value={dash.id}>
{dash.name || <span style={{ color: 'var(--muted)' }}>Untitled</span>}
{dash.is_shared && (
<Tooltip title="This dashboard is publicly shared">
<ShareAltOutlined style={{ marginLeft: 4, float: 'right' }} />
</Tooltip>
)}
</Select.Option>
))}
<Select.Option value="new">+ New Dashboard</Select.Option>
</Select>
</div>
)}
<div className="dashboard-meta">
{dashboardMode === DashboardMode.Edit
? actionsEditMode
: dashboardMode === DashboardMode.Fullscreen
? actionsPresentationMode
: actionsDefault}
</div>
</>
)}
</div>
{user?.organization?.available_features?.includes(AvailableFeature.DASHBOARD_COLLABORATION) && (
<>
<div className="mb" data-attr="dashboard-tags">
<ObjectTags
tags={dashboard.tags}
onTagSave={saveNewTag}
onTagDelete={deleteTag}
saving={dashboardLoading}
tagsAvailable={dashboardTags.filter((tag) => !dashboard.tags.includes(tag))}
/>
</div>
<Description
item={dashboard}
setItemMode={setDashboardMode}
itemMode={dashboardMode}
triggerItemUpdate={triggerDashboardUpdate}
descriptionInputRef={descriptionInputRef}
/>
</>
)}
</>
)
}
Example #6
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 #7
Source File: BuilderToolbar.spec.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
describe("BuilderToolbar", () => {
beforeEach(() => {
jest.clearAllMocks();
});
it("should work", async () => {
mockUseBuilderUIContext.mockReturnValue({
onCurrentRouteClick: mockCurrentRouteClick,
onBuildAndPush: mockBuildAndPush,
onPreview: mockPreview,
dataType: BuilderDataType.ROUTE_OF_BRICKS,
});
mockUseBuilderNode.mockReturnValue({
id: "R-01",
type: "bricks",
path: "/",
});
const wrapper = shallow(<BuilderToolbar />);
expect(wrapper.find(".tabLink").length).toBe(6);
wrapper.find(".tabLink[data-testid='view-route']").simulate("click");
expect(mockCurrentRouteClick).toBeCalledWith({
id: "R-01",
type: "bricks",
path: "/",
});
wrapper.find(".tabLink[data-testid='build-and-push']").simulate("click");
expect(mockBuildAndPush).toBeCalled();
wrapper.find(".tabLink[data-testid='preview']").simulate("click");
expect(mockPreview).toBeCalled();
});
it("should work with custom template", async () => {
mockUseBuilderUIContext.mockReturnValue({
onCurrentTemplateClick: mockCurrentTemplateClick,
onCurrentSnippetClick: mockCurrentSnippetClick,
onCurrentRouteClick: mockCurrentRouteClick,
onBuildAndPush: mockBuildAndPush,
onPreview: mockPreview,
dataType: BuilderDataType.CUSTOM_TEMPLATE,
});
mockUseBuilderNode.mockReturnValue({
id: "T-01",
type: "custom-template",
templateId: "tpl-test",
});
const wrapper = shallow(<BuilderToolbar />);
expect(wrapper.find(".tabLink").length).toBe(6);
expect(
wrapper.find(".tabLink").filter("[data-testid='view-template']").length
).toBe(1);
wrapper
.find(".tabLink")
.filter("[data-testid='view-template']")
.simulate("click");
expect(mockCurrentTemplateClick).toBeCalled();
});
it("should work with snippet", async () => {
mockUseBuilderUIContext.mockReturnValue({
onCurrentTemplateClick: mockCurrentTemplateClick,
onCurrentSnippetClick: mockCurrentSnippetClick,
onCurrentRouteClick: mockCurrentRouteClick,
onBuildAndPush: mockBuildAndPush,
onPreview: mockPreview,
dataType: BuilderDataType.SNIPPET,
});
mockUseBuilderNode.mockReturnValue({
id: "S-01",
type: "snippet",
snippetId: "snippet-test",
});
const wrapper = shallow(<BuilderToolbar />);
expect(wrapper.find(".tabLink").length).toBe(6);
expect(
wrapper.find(".tabLink").filter("[data-testid='view-snippet']").length
).toBe(1);
wrapper
.find(".tabLink")
.filter("[data-testid='view-snippet']")
.simulate("click");
expect(mockCurrentSnippetClick).toBeCalled();
});
it("should enter fullscreen", () => {
let fullscreen = false;
const setFullscreen = jest.fn((update) => {
fullscreen = update(fullscreen);
});
mockUseBuilderUIContext.mockImplementation(() => ({
fullscreen,
setFullscreen,
}));
const wrapper = shallow(<BuilderToolbar />);
expect(wrapper.find(FullscreenOutlined).length).toBe(1);
expect(wrapper.find(FullscreenExitOutlined).length).toBe(0);
wrapper.find(".tabLink[data-testid='toggle-fullscreen']").invoke("onClick")(
null
);
expect(setFullscreen).toBeCalled();
expect(fullscreen).toBe(true);
});
it("should invoke onWorkbenchClose", () => {
const onWorkbenchClose = jest.fn();
mockUseBuilderUIContext.mockImplementation(() => ({
onWorkbenchClose,
}));
const wrapper = shallow(<BuilderToolbar />);
wrapper.find(".tabLink[data-testid='workbench-close']").invoke("onClick")(
null
);
expect(onWorkbenchClose).toBeCalled();
});
it("should show layer viewer ", () => {
(getRuntime as jest.Mock).mockReturnValueOnce({
getFeatureFlags: jest
.fn()
.mockReturnValue({ "next-builder-layer-view": true }),
});
const wrapper = shallow(<BuilderToolbar />);
expect(wrapper.find(LibraryDropdown).length).toEqual(3);
wrapper.find(LibraryDropdown).at(0).invoke("onVisbleChange")(true);
wrapper.find(LibraryDropdown).at(1).invoke("onVisbleChange")(true);
wrapper.find(LibraryDropdown).at(2).invoke("onVisbleChange")(true);
const tooltipWrapper = wrapper.find(LibraryDropdown).at(0).shallow();
expect(tooltipWrapper.find(Tooltip).at(0).prop("overlayStyle")).toEqual({
display: "none",
});
});
it("should show the hidden wrapper switch", () => {
mockUseBuilderUIContext.mockReturnValue({
onCurrentRouteClick: mockCurrentRouteClick,
onBuildAndPush: mockBuildAndPush,
onPreview: mockPreview,
dataType: BuilderDataType.ROUTE_OF_BRICKS,
hiddenWrapper: true,
setHiddenWrapper: mockSetHiddenWrapper,
});
mockUseBuilderNode.mockReturnValue({
id: "R-01",
type: "bricks",
path: "/",
});
mockUseBuilderData.mockReturnValue({
wrapperNode: {
$$uid: 1,
},
} as any);
const wrapper = shallow(<BuilderToolbar />);
expect(wrapper.find(Switch).length).toBe(1);
wrapper.find(Switch).prop("onChange")(true, {} as MouseEvent);
expect(mockSetHiddenWrapper).toBeCalled();
});
});
Example #8
Source File: BuilderToolbar.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
export function BuilderToolbar(): React.ReactElement {
const { t } = useTranslation(NS_NEXT_BUILDER);
const enableLayerView = React.useMemo(
() => getRuntime().getFeatureFlags()["next-builder-layer-view"],
[]
);
const [libsDropdownVisible, setLibsDropdownVisible] = useState<{
[key in typeof LayerType[keyof typeof LayerType]]: boolean;
}>({
[LayerType.LAYOUT]: false,
[LayerType.WIDGET]: false,
[LayerType.BRICK]: false,
});
const { wrapperNode } = useBuilderData();
const {
onCurrentRouteClick,
onCurrentTemplateClick,
onCurrentSnippetClick,
onBuildAndPush,
onPreview,
dataType,
fullscreen,
setFullscreen,
onWorkbenchClose,
hiddenWrapper,
setHiddenWrapper,
} = useBuilderUIContext();
const rootNode = useBuilderNode({ isRoot: true });
const handleRouteClick = (): void => {
onCurrentRouteClick?.(rootNode as BuilderRouteNode);
};
const handleTemplateClick = (): void => {
onCurrentTemplateClick?.(rootNode as BuilderCustomTemplateNode);
};
const handleSnippetClick = (): void => {
onCurrentSnippetClick?.(rootNode as BuilderSnippetNode);
};
const handlePreview = (): void => {
onPreview?.();
};
const handleBuildAndPush = (): void => {
onBuildAndPush?.();
};
const handleToggleFullscreen = React.useCallback(() => {
setFullscreen((prev) => !prev);
}, [setFullscreen]);
const handleClose = (): void => {
onWorkbenchClose?.();
};
const divider = useMemo(
() => <Divider type="vertical" style={{ height: 25 }} />,
[]
);
return (
<div className={styles.toolbarContainer}>
<div className={styles.toolbarLeft}>
{dataType === BuilderDataType.SNIPPET ? (
<Tooltip title={t(K.VIEW_SNIPPET)} placement="bottomLeft">
<a
className={shareStyles.tabLink}
role="button"
onClick={handleSnippetClick}
data-testid="view-snippet"
>
<BlockOutlined />
</a>
</Tooltip>
) : dataType === BuilderDataType.CUSTOM_TEMPLATE ? (
<Tooltip title={t(K.VIEW_TEMPLATE)} placement="bottomLeft">
<a
className={shareStyles.tabLink}
role="button"
onClick={handleTemplateClick}
data-testid="view-template"
>
<BlockOutlined />
</a>
</Tooltip>
) : (
<Tooltip title={t(K.VIEW_ROUTE)} placement="bottomLeft">
<a
className={shareStyles.tabLink}
role="button"
onClick={handleRouteClick}
data-testid="view-route"
>
<BranchesOutlined />
</a>
</Tooltip>
)}
<RootNodeSelect />
</div>
<div className={styles.toolbarRight}>
{wrapperNode ? (
<Switch
checkedChildren="显示布局"
unCheckedChildren="隐藏布局"
checked={hiddenWrapper}
onChange={setHiddenWrapper}
size="small"
style={{
marginRight: 10,
top: -1,
}}
/>
) : null}
{enableLayerView && (
<>
<LibraryDropdown
menuItems={layoutMenus}
type={LayerType.LAYOUT}
onVisbleChange={(visible) =>
setLibsDropdownVisible({
...libsDropdownVisible,
[LayerType.LAYOUT]: visible,
})
}
>
<Tooltip
title={t(K.LAYOUT_LIBRARY)}
placement="bottomRight"
overlayStyle={{
// Hide tooltip when dropdown is open.
display: libsDropdownVisible[LayerType.LAYOUT]
? "none"
: undefined,
}}
>
<Button
type="link"
size="small"
className={shareStyles.tabLink}
style={{ marginRight: "10px" }}
>
<LayoutOutlined />
</Button>
</Tooltip>
</LibraryDropdown>
<LibraryDropdown
menuItems={widgetMenus}
type={LayerType.WIDGET}
onVisbleChange={(visible) =>
setLibsDropdownVisible({
...libsDropdownVisible,
[LayerType.WIDGET]: visible,
})
}
>
<Tooltip
title={t(K.WIDGET_LIBRARY)}
placement="bottomRight"
overlayStyle={{
display: libsDropdownVisible[LayerType.WIDGET]
? "none"
: undefined,
}}
>
<Button
type="link"
size="small"
className={shareStyles.tabLink}
style={{ marginRight: "10px" }}
>
<GoldOutlined />
</Button>
</Tooltip>
</LibraryDropdown>
</>
)}
<LibraryDropdown
menuItems={brickMenus}
type={LayerType.BRICK}
onVisbleChange={(visible) =>
setLibsDropdownVisible({
...libsDropdownVisible,
[LayerType.BRICK]: visible,
})
}
>
<Tooltip
title={t(K.BRICK_LIBRARY)}
placement="bottomRight"
overlayStyle={{
display: libsDropdownVisible[LayerType.BRICK]
? "none"
: undefined,
}}
>
<Button
type="link"
size="small"
style={{ marginRight: "10px" }}
className={shareStyles.tabLink}
>
<PlusOutlined />
</Button>
</Tooltip>
</LibraryDropdown>
{divider}
<Tooltip title={t(K.BUILD_AND_PUSH_TOOLTIP)} placement="bottomRight">
<a
className={shareStyles.tabLink}
role="button"
onClick={handleBuildAndPush}
data-testid="build-and-push"
>
<ApiOutlined />
</a>
</Tooltip>
<Tooltip title={t(K.PREVIEW)} placement="bottomRight">
<a
className={shareStyles.tabLink}
role="button"
onClick={handlePreview}
data-testid="preview"
>
<CaretRightOutlined />
</a>
</Tooltip>
{divider}
<SettingDropdown />
{!fullscreen && (
<Tooltip
title={t(fullscreen ? K.EXIT_FULLSCREEN : K.ENTER_FULLSCREEN)}
placement="bottomRight"
>
<a
className={shareStyles.tabLink}
role="button"
onClick={handleToggleFullscreen}
data-testid="toggle-fullscreen"
>
{fullscreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
</a>
</Tooltip>
)}
<Tooltip title={t(K.CLOSE)} placement="bottomRight">
<a
className={shareStyles.tabLink}
role="button"
onClick={handleClose}
data-testid="workbench-close"
>
<CloseOutlined />
</a>
</Tooltip>
</div>
</div>
);
}
Example #9
Source File: WidgetActionDropdown.tsx From datart with Apache License 2.0 | 4 votes |
WidgetActionDropdown: React.FC<WidgetActionDropdownProps> = memo(
({ widget }) => {
const { editing: boardEditing } = useContext(BoardContext);
const widgetAction = useWidgetAction();
const dataChart = useContext(WidgetChartContext)!;
const t = useI18NPrefix(`viz.widget.action`);
const menuClick = useCallback(
({ key }) => {
widgetAction(key, widget);
},
[widgetAction, widget],
);
const getAllList = useCallback(() => {
const allWidgetActionList: WidgetActionListItem<widgetActionType>[] = [
{
key: 'refresh',
label: t('refresh'),
icon: <SyncOutlined />,
},
{
key: 'fullScreen',
label: t('fullScreen'),
icon: <FullscreenOutlined />,
},
{
key: 'edit',
label: t('edit'),
icon: <EditOutlined />,
},
{
key: 'delete',
label: t('delete'),
icon: <DeleteOutlined />,
danger: true,
},
{
key: 'info',
label: t('info'),
icon: <InfoOutlined />,
},
{
key: 'lock',
label: t('lock'),
icon: <LockOutlined />,
},
{
key: 'makeLinkage',
label: t('makeLinkage'),
icon: <LinkOutlined />,
divider: true,
},
{
key: 'closeLinkage',
label: t('closeLinkage'),
icon: <CloseCircleOutlined />,
danger: true,
},
{
key: 'makeJump',
label: t('makeJump'),
icon: <BranchesOutlined />,
divider: true,
},
{
key: 'closeJump',
label: t('closeJump'),
icon: <CloseCircleOutlined />,
danger: true,
},
];
return allWidgetActionList;
}, [t]);
const actionList = useMemo(() => {
return (
getWidgetActionList({
allList: getAllList(),
widget,
boardEditing,
chartGraphId: dataChart?.config?.chartGraphId,
}) || []
);
}, [boardEditing, dataChart?.config?.chartGraphId, getAllList, widget]);
const dropdownList = useMemo(() => {
const menuItems = actionList.map(item => {
return (
<React.Fragment key={item.key}>
{item.divider && <Menu.Divider />}
<Menu.Item
danger={item.danger}
icon={item.icon}
disabled={item.disabled}
key={item.key}
>
{item.label}
</Menu.Item>
</React.Fragment>
);
});
return <Menu onClick={menuClick}>{menuItems}</Menu>;
}, [actionList, menuClick]);
if (actionList.length === 0) {
return null;
}
return (
<Dropdown
className="widget-tool-dropdown"
overlay={dropdownList}
placement="bottomCenter"
trigger={['click']}
arrow
>
<Button icon={<EllipsisOutlined />} type="link" />
</Dropdown>
);
},
)
Example #10
Source File: useExtraBtn.tsx From amiya with MIT License | 4 votes |
export default function useExtraBtn(
tableRef: any,
searchRef: any,
tableFields: Array<AyTableField>,
setTableFields: Dispatch<React.SetStateAction<AyTableField[]>>,
props: AySearchTableProps
) {
// 合并配置
const config = Object.assign({}, defaultConfig, props)
const {
extraVisible,
extraRefreshVisible,
extraSizeVisible,
extraSizeDefaultValue,
extraSettingVisible,
extraFullscreenVisible
} = config
/** 表格尺寸 */
const [size, setSize] = useState<SizeType>(extraSizeDefaultValue)
/** 表格全屏 */
const [isEnter, setIsEnter] = useState<boolean>(false)
const fieldsEdit = useFieldsEdit(tableFields, setTableFields)
const handleRefresh = () => {
tableRef.current.refresh()
}
const handleSizeChange = (e: any) => {
setSize(e.key)
}
useEffect(() => {
// body 的 style 防止滚动
if (isEnter) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = ''
}
searchRef.current && searchRef.current.resize()
}, [isEnter])
const extraBtns = extraVisible ? (
<div className="ay-search-table-extra-btns" key="ay-search-table-extra-btns">
<Space size="middle">
{extraRefreshVisible ? (
<Tooltip title={locale.extra.refresh}>
<ReloadOutlined onClick={handleRefresh} />
</Tooltip>
) : null}
{extraSizeVisible ? (
<Tooltip title={locale.extra.density}>
<Dropdown
overlay={
<Menu style={{ width: 100 }} selectedKeys={[size + '']} onClick={handleSizeChange}>
<Menu.Item key="large">{locale.extra.densityLarger}</Menu.Item>
<Menu.Item key="middle">{locale.extra.densityMiddle}</Menu.Item>
<Menu.Item key="small">{locale.extra.densitySmall}</Menu.Item>
</Menu>
}
>
<ColumnHeightOutlined />
</Dropdown>
</Tooltip>
) : null}
{extraSettingVisible ? fieldsEdit : null}
{extraFullscreenVisible ? (
isEnter ? (
<Tooltip title={locale.extra.exitFullScreen} key={locale.extra.exitFullScreen}>
<FullscreenExitOutlined className="ay-search-table-fullscrenn-enter" onClick={() => setIsEnter(false)} />
</Tooltip>
) : (
<Tooltip title={locale.extra.fullScreen} key={locale.extra.fullScreen}>
<FullscreenOutlined className="ay-search-table-fullscrenn-out" onClick={() => setIsEnter(true)} />
</Tooltip>
)
) : null}
</Space>
</div>
) : null
return {
extraBtns,
isEnter,
setIsEnter,
size
}
}
Example #11
Source File: YakExecutor.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
YakExecutor: React.FC<YakExecutorProp> = (props) => {
const [codePath, setCodePath] = useState<string>("")
const [loading, setLoading] = useState<boolean>(false)
const [fileList, setFileList] = useState<tabCodeProps[]>([])
const [tabList, setTabList] = useState<tabCodeProps[]>([])
const [activeTab, setActiveTab] = useState<string>("")
const [unTitleCount, setUnTitleCount] = useState(1)
const [hintShow, setHintShow] = useState<boolean>(false)
const [hintFile, setHintFile] = useState<string>("")
const [hintIndex, setHintIndex] = useState<number>(0)
const [renameHint, setRenameHint] = useState<boolean>(false)
const [renameIndex, setRenameIndex] = useState<number>(-1)
const [renameFlag, setRenameFlag] = useState<boolean>(false)
const [renameCache, setRenameCache] = useState<string>("")
const [fullScreen, setFullScreen] = useState<boolean>(false)
const [errors, setErrors] = useState<string[]>([])
const [executing, setExecuting] = useState(false)
const [outputEncoding, setOutputEncoding] = useState<"utf8" | "latin1">("utf8")
const xtermAsideRef = useRef(null)
const xtermRef = useRef(null)
const timer = useRef<any>(null)
const [extraParams, setExtraParams] = useState("")
// trigger for updating
const [triggerForUpdatingHistory, setTriggerForUpdatingHistory] = useState<any>(0)
const addFileTab = useMemoizedFn((res: any) => {
const {name, code} = res
const tab: tabCodeProps = {
tab: `${name}.yak`,
code: code,
suffix: "yak",
isFile: false
}
setActiveTab(`${tabList.length}`)
setTabList(tabList.concat([tab]))
setUnTitleCount(unTitleCount + 1)
})
useEffect(() => {
ipcRenderer.on("fetch-send-to-yak-running", (e, res: any) => addFileTab(res))
return () => ipcRenderer.removeAllListeners("fetch-send-to-yak-running")
}, [])
// 自动保存
const autoSave = useMemoizedFn(() => {
for (let tabInfo of tabList) {
if (tabInfo.isFile) {
ipcRenderer.invoke("write-file", {
route: tabInfo.route,
data: tabInfo.code
})
}
}
})
// 保存近期文件内的15个
const saveFiliList = useMemoizedFn(() => {
let files = cloneDeep(fileList).reverse()
files.splice(14)
files = files.reverse()
ipcRenderer.invoke("set-value", RecentFileList, files)
})
// 获取和保存近期打开文件信息,同时展示打开默认内容
useEffect(() => {
let time: any = null
let timer: any = null
setLoading(true)
ipcRenderer
.invoke("get-value", RecentFileList)
.then((value: any) => {
if ((value || []).length !== 0) {
setFileList(value)
} else {
const tab: tabCodeProps = {
tab: `Untitle-${unTitleCount}.yak`,
code: "# input your yak code\nprintln(`Hello Yak World!`)",
suffix: "yak",
isFile: false
}
setActiveTab(`${tabList.length}`)
setTabList([tab])
setUnTitleCount(unTitleCount + 1)
}
})
.catch(() => {})
.finally(() => {
setTimeout(() => setLoading(false), 300)
time = setInterval(() => {
autoSave()
}, 2000)
timer = setInterval(() => {
saveFiliList()
}, 5000)
})
return () => {
saveFiliList()
if (time) clearInterval(time)
if (timer) clearInterval(timer)
}
}, [])
// 全局监听重命名事件是否被打断
useEffect(() => {
document.onmousedown = (e) => {
// @ts-ignore
if (e.path[0].id !== "rename-input" && renameFlag) {
renameCode(renameIndex)
setRenameFlag(false)
}
}
}, [renameFlag])
// 打开文件
const addFile = useMemoizedFn((file: any) => {
const isExists = fileList.filter((item) => item.tab === file.name && item.route === file.path).length === 1
if (isExists) {
for (let index in tabList) {
const item = tabList[index]
if (item.tab === file.name && item.route === file.path) {
setActiveTab(`${index}`)
return false
}
}
}
ipcRenderer
.invoke("fetch-file-content", file.path)
.then((res) => {
const tab: tabCodeProps = {
tab: file.name,
code: res,
suffix: file.name.split(".").pop() === "yak" ? "yak" : "http",
isFile: true,
route: file.path,
extraParams: file.extraParams
}
setActiveTab(`${tabList.length}`)
if (!isExists) setFileList(fileList.concat([tab]))
setTabList(tabList.concat([tab]))
})
.catch(() => {
failed("无法获取该文件内容,请检查后后重试!")
const files = cloneDeep(fileList)
for (let i in files) if (files[i].route === file.path) files.splice(i, 1)
setFileList(files)
})
return false
})
// 新建文件
const newFile = useMemoizedFn(() => {
const tab: tabCodeProps = {
tab: `Untitle-${unTitleCount}.yak`,
code: "# input your yak code\nprintln(`Hello Yak World!`)",
suffix: "yak",
isFile: false
}
setActiveTab(`${tabList.length}`)
setTabList(tabList.concat([tab]))
setUnTitleCount(unTitleCount + 1)
})
//修改文件
const modifyCode = useMemoizedFn((value: string, index: number) => {
const tabs = cloneDeep(tabList)
tabs[index].code = value
setTabList(tabs)
})
// 保存文件
const saveCode = useMemoizedFn((info: tabCodeProps, index: number) => {
if (info.isFile) {
ipcRenderer.invoke("write-file", {
route: info.route,
data: info.code
})
} else {
ipcRenderer.invoke("show-save-dialog", `${codePath}${codePath ? '/' : ''}${info.tab}`).then((res) => {
if (res.canceled) return
const path = res.filePath
const name = res.name
ipcRenderer
.invoke("write-file", {
route: res.filePath,
data: info.code
})
.then(() => {
const suffix = name.split(".").pop()
var tabs = cloneDeep(tabList)
var active = null
tabs = tabs.filter((item) => item.route !== path)
tabs = tabs.map((item, index) => {
if (!item.route && item.tab === info.tab) {
active = index
item.tab = name
item.isFile = true
item.suffix = suffix === "yak" ? suffix : "http"
item.route = path
return item
}
return item
})
if (active !== null) setActiveTab(`${active}`)
setTabList(tabs)
const file: tabCodeProps = {
tab: name,
code: info.code,
isFile: true,
suffix: suffix === "yak" ? suffix : "http",
route: res.filePath,
extraParams: info.extraParams
}
for (let item of fileList) {
if (item.route === file.route) {
return
}
}
setFileList(fileList.concat([file]))
})
})
}
})
//关闭文件
const closeCode = useMemoizedFn((index, isFileList: boolean) => {
const tabInfo = isFileList ? fileList[+index] : tabList[+index]
if (isFileList) {
for (let i in tabList) {
if (tabList[i].tab === tabInfo.tab && tabList[i].route === tabInfo.route) {
const tabs = cloneDeep(tabList)
tabs.splice(i, 1)
setTabList(tabs)
setActiveTab(tabs.length >= 1 ? `0` : "")
}
}
const files = cloneDeep(fileList)
files.splice(+index, 1)
setFileList(files)
} else {
setActiveTab(index)
if (!tabInfo.isFile) {
setHintFile(tabInfo.tab)
setHintIndex(index)
setHintShow(true)
} else {
const tabs = cloneDeep(tabList)
tabs.splice(+index, 1)
setTabList(tabs)
setActiveTab(tabs.length >= 1 ? `0` : "")
}
}
})
// 关闭虚拟文件不保存
const ownCloseCode = useMemoizedFn(() => {
const tabs = cloneDeep(tabList)
tabs.splice(hintIndex, 1)
setTabList(tabs)
setHintShow(false)
setActiveTab(tabs.length >= 1 ? `0` : "")
})
// 删除文件
const delCode = useMemoizedFn((index) => {
const fileInfo = fileList[index]
ipcRenderer
.invoke("delelte-code-file", fileInfo.route)
.then(() => {
for (let i in tabList) {
if (tabList[i].tab === fileInfo.tab && tabList[i].route === fileInfo.route) {
const tabs = cloneDeep(tabList)
tabs.splice(i, 1)
setTabList(tabs)
setActiveTab(tabs.length >= 1 ? `0` : "")
}
}
const arr = cloneDeep(fileList)
arr.splice(index === undefined ? hintIndex : index, 1)
setFileList(arr)
})
.catch(() => {
failed("文件删除失败!")
})
})
//重命名操作
const renameCode = useMemoizedFn((index: number) => {
const tabInfo = fileList[index]
if (renameCache === tabInfo.tab) return
if (!renameCache) return
if (!tabInfo.route) return
const flagStr = tabInfo.route?.indexOf("/") > -1 ? "/" : "\\"
const routes = tabInfo.route?.split(flagStr)
routes?.pop()
ipcRenderer
.invoke("is-exists-file", routes?.concat([renameCache]).join(flagStr))
.then(() => {
const newRoute = routes?.concat([renameCache]).join(flagStr)
if (!tabInfo.route || !newRoute) return
renameFile(index, renameCache, tabInfo.route, newRoute)
})
.catch(() => {
setRenameHint(true)
})
})
// 重命名文件
const renameFile = useMemoizedFn(
(index: number, rename: string, oldRoute: string, newRoute: string, callback?: () => void) => {
ipcRenderer.invoke("rename-file", {old: oldRoute, new: newRoute}).then(() => {
const suffix = rename.split(".").pop()
var files = cloneDeep(fileList)
var tabs = cloneDeep(tabList)
var active = null
files = files.filter((item) => item.route !== newRoute)
tabs = tabs.filter((item) => item.route !== newRoute)
files = files.map((item) => {
if (item.route === oldRoute) {
item.tab = rename
item.suffix = suffix === "yak" ? suffix : "http"
item.route = newRoute
return item
}
return item
})
tabs = tabs.map((item, index) => {
if (item.route === oldRoute) {
active = index
item.tab = rename
item.suffix = suffix === "yak" ? suffix : "http"
item.route = newRoute
return item
}
return item
})
if (active !== null) setActiveTab(`${active}`)
setFileList(files)
setTabList(tabs)
if (callback) callback()
})
}
)
const fileFunction = (kind: string, index: string, isFileList: boolean) => {
const tabCodeInfo = isFileList ? fileList[index] : tabList[index]
switch (kind) {
case "own":
closeCode(index, isFileList)
return
case "other":
const tabInfo: tabCodeProps = cloneDeep(tabList[index])
for (let i in tabList) {
if (i !== index && !tabList[i].isFile) {
const arr: tabCodeProps[] =
+i > +index
? [tabInfo].concat(tabList.splice(+i, tabList.length))
: tabList.splice(+i, tabList.length)
const num = +i > +index ? 1 : 0
setActiveTab(`${num}`)
setTabList(arr)
setHintFile(arr[num].tab)
setHintIndex(num)
setHintShow(true)
return
}
}
const code = cloneDeep(tabList[index])
setTabList([code])
setActiveTab(`0`)
return
case "all":
for (let i in tabList) {
if (!tabList[i].isFile) {
const arr = tabList.splice(+i, tabList.length)
setActiveTab("0")
setTabList(arr)
setHintFile(arr[0].tab)
setHintIndex(0)
setHintShow(true)
return
}
}
setActiveTab("")
setTabList([])
return
case "remove":
closeCode(index, isFileList)
return
case "delete":
delCode(index)
return
case "rename":
setRenameIndex(+index)
setRenameFlag(true)
setRenameCache(tabCodeInfo.tab)
return
}
}
const openFileLayout = (file: any) => {
addFile(file)
}
useEffect(() => {
ipcRenderer.invoke("fetch-code-path")
.then((path: string) => {
ipcRenderer.invoke("is-exists-file", path)
.then(() => {
setCodePath("")
})
.catch(() => {
setCodePath(path)
})
})
}, [])
useEffect(() => {
if (tabList.length === 0) setFullScreen(false)
}, [tabList])
useEffect(() => {
if (!xtermRef) {
return
}
// let buffer = "";
ipcRenderer.on("client-yak-error", async (e: any, data) => {
failed(`FoundError: ${JSON.stringify(data)}`)
if (typeof data === "object") {
setErrors([...errors, `${JSON.stringify(data)}`])
} else if (typeof data === "string") {
setErrors([...errors, data])
} else {
setErrors([...errors, `${data}`])
}
})
ipcRenderer.on("client-yak-end", () => {
info("Yak 代码执行完毕")
setTriggerForUpdatingHistory(getRandomInt(100000))
setTimeout(() => {
setExecuting(false)
}, 300)
})
ipcRenderer.on("client-yak-data", async (e: any, data: ExecResult) => {
if (data.IsMessage) {
// alert(Buffer.from(data.Message).toString("utf8"))
}
if (data?.Raw) {
writeExecResultXTerm(xtermRef, data, outputEncoding)
// writeXTerm(xtermRef, Buffer.from(data.Raw).toString(outputEncoding).replaceAll("\n", "\r\n"))
// monacoEditorWrite(currentOutputEditor, )
}
})
return () => {
ipcRenderer.removeAllListeners("client-yak-data")
ipcRenderer.removeAllListeners("client-yak-end")
ipcRenderer.removeAllListeners("client-yak-error")
}
}, [xtermRef])
const bars = (props: any, TabBarDefault: any) => {
return (
<TabBarDefault
{...props}
children={(barNode: React.ReactElement) => {
return (
<Dropdown
overlay={CustomMenu(barNode.key, false, tabMenu, fileFunction)}
trigger={["contextMenu"]}
>
{barNode}
</Dropdown>
)
}}
/>
)
}
return (
<AutoCard
className={"yak-executor-body"}
// title={"Yak Runner"}
headStyle={{minHeight: 0}}
bodyStyle={{padding: 0, overflow: "hidden"}}
>
<div
style={{width: "100%", height: "100%", display: "flex", backgroundColor: "#E8E9E8"}}
tabIndex={0}
onKeyDown={(e) => {
if (e.keyCode === 78 && (e.ctrlKey || e.metaKey)) {
newFile()
}
if (e.keyCode === 83 && (e.ctrlKey || e.metaKey) && activeTab) {
saveCode(tabList[+activeTab], +activeTab)
}
}}
>
<div style={{width: `${fullScreen ? 0 : 15}%`}}>
<AutoSpin spinning={loading}>
<ExecutorFileList
lists={fileList}
activeFile={tabList[+activeTab]?.route || ""}
renameFlag={renameFlag}
renameIndex={renameIndex}
renameCache={renameCache}
setRenameCache={setRenameCache}
addFile={addFile}
newFile={newFile}
openFile={openFileLayout}
fileFunction={fileFunction}
/>
</AutoSpin>
</div>
<div style={{width: `${fullScreen ? 100 : 85}%`}} className='executor-right-body'>
{tabList.length > 0 && (
<ResizeBox
isVer
firstNode={
<Tabs
className={"right-editor"}
style={{height: "100%"}}
type='editable-card'
activeKey={activeTab}
hideAdd={true}
onChange={(activeTab) => setActiveTab(activeTab)}
onEdit={(key, event: "add" | "remove") => {
switch (event) {
case "remove":
closeCode(key, false)
return
case "add":
return
}
}}
renderTabBar={(props, TabBarDefault) => {
return bars(props, TabBarDefault)
}}
tabBarExtraContent={
tabList.length && (
<Space style={{marginRight: 5}} size={0}>
<Button
style={{height: 25}}
type={"link"}
size={"small"}
disabled={
tabList[+activeTab] && tabList[+activeTab].suffix !== "yak"
}
onClick={(e) => {
let m = showDrawer({
width: "60%",
placement: "left",
title: "选择你的 Yak 模块执行特定功能",
content: (
<>
<YakScriptManagerPage
type={"yak"}
onLoadYakScript={(s) => {
const tab: tabCodeProps = {
tab: `Untitle-${unTitleCount}.yak`,
code: s.Content,
suffix: "yak",
isFile: false
}
info(`加载 Yak 模块:${s.ScriptName}`)
xtermClear(xtermRef)
setActiveTab(`${tabList.length}`)
setTabList(tabList.concat([tab]))
setUnTitleCount(unTitleCount + 1)
m.destroy()
}}
/>
</>
)
})
}}
>
Yak脚本模板
</Button>
<Button
icon={
fullScreen ? (
<FullscreenExitOutlined style={{fontSize: 15}} />
) : (
<FullscreenOutlined style={{fontSize: 15}} />
)
}
type={"link"}
size={"small"}
style={{width: 30, height: 25}}
onClick={() => setFullScreen(!fullScreen)}
/>
<Popover
trigger={["click"]}
title={"设置命令行额外参数"}
placement="bottomRight"
content={
<Space style={{width: 400}}>
<div>yak {tabList[+activeTab]?.tab || "[file]"}</div>
<Divider type={"vertical"} />
<Paragraph
style={{width: 200, marginBottom: 0}}
editable={{
icon: <Space>
<EditOutlined />
<SaveOutlined onClick={(e) => {
e.stopPropagation()
tabList[+activeTab].extraParams = extraParams
setTabList(tabList)
if(tabList[+activeTab].isFile){
const files = fileList.map(item => {
if(item.route === tabList[+activeTab].route){
item.extraParams = extraParams
return item
}
return item
})
setFileList(files)
}
success("保存成功")
}}
/></Space>,
tooltip: '编辑/保存为该文件默认参数',
onChange: setExtraParams
}}
>
{extraParams}
</Paragraph>
</Space>
}
>
<Button type={"link"} icon={<EllipsisOutlined />} onClick={() => {
setExtraParams(tabList[+activeTab]?.extraParams || "")
}} />
</Popover>
{executing ? (
<Button
icon={<PoweroffOutlined style={{fontSize: 15}} />}
type={"link"}
danger={true}
size={"small"}
style={{width: 30, height: 25}}
onClick={() => ipcRenderer.invoke("cancel-yak")}
/>
) : (
<Button
icon={<CaretRightOutlined style={{fontSize: 15}} />}
type={"link"}
ghost={true}
size={"small"}
style={{width: 30, height: 25}}
disabled={
tabList[+activeTab] && tabList[+activeTab].suffix !== "yak"
}
onClick={() => {
setErrors([])
setExecuting(true)
ipcRenderer.invoke("exec-yak", {
Script: tabList[+activeTab].code,
Params: [],
RunnerParamRaw: extraParams
})
}}
/>
)}
</Space>
)
}
>
{tabList.map((item, index) => {
return (
<TabPane tab={item.tab} key={`${index}`}>
<div style={{height: "100%"}}>
<AutoSpin spinning={executing}>
<div style={{height: "100%"}}>
<YakEditor
type={item.suffix}
value={item.code}
setValue={(value) => {
modifyCode(value, index)
}}
/>
</div>
</AutoSpin>
</div>
</TabPane>
)
})}
</Tabs>
}
firstRatio='70%'
secondNode={
<div
ref={xtermAsideRef}
style={{
width: "100%",
height: "100%",
overflow: "hidden",
borderTop: "1px solid #dfdfdf"
}}
>
<Tabs
style={{height: "100%"}}
className={"right-xterm"}
size={"small"}
tabBarExtraContent={
<Space>
<SelectOne
formItemStyle={{marginBottom: 0}}
value={outputEncoding}
setValue={setOutputEncoding}
size={"small"}
data={[
{text: "GBxxx编码", value: "latin1"},
{text: "UTF-8编码", value: "utf8"}
]}
/>
<Button
size={"small"}
icon={<DeleteOutlined />}
type={"link"}
onClick={(e) => {
xtermClear(xtermRef)
}}
/>
</Space>
}
>
<TabPane
tab={<div style={{width: 50, textAlign: "center"}}>输出</div>}
key={"output"}
>
<div style={{width: "100%", height: "100%"}}>
<CVXterm
ref={xtermRef}
options={{
convertEol: true,
theme: {
foreground: "#536870",
background: "#E8E9E8",
cursor: "#536870",
black: "#002831",
brightBlack: "#001e27",
red: "#d11c24",
brightRed: "#bd3613",
green: "#738a05",
brightGreen: "#475b62",
yellow: "#a57706",
brightYellow: "#536870",
blue: "#2176c7",
brightBlue: "#708284",
magenta: "#c61c6f",
brightMagenta: "#5956ba",
cyan: "#259286",
brightCyan: "#819090",
white: "#eae3cb",
brightWhite: "#fcf4dc"
}
}}
/>
</div>
</TabPane>
<TabPane
tab={
<div style={{width: 50, textAlign: "center"}} key={"terminal"}>
终端(监修中)
</div>
}
disabled
>
<Terminal />
</TabPane>
</Tabs>
</div>
}
secondRatio='30%'
/>
)}
{tabList.length === 0 && (
<Empty className='right-empty' description={<p>请点击左侧打开或新建文件</p>}></Empty>
)}
</div>
<Modal
visible={hintShow}
onCancel={() => setHintShow(false)}
footer={[
<Button key='link' onClick={() => setHintShow(false)}>
取消
</Button>,
<Button key='submit' onClick={() => ownCloseCode()}>
不保存
</Button>,
<Button key='back' type='primary' onClick={() => saveCode(tabList[hintIndex], hintIndex)}>
保存
</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}}>{`是否要保存${hintFile}里面的内容吗?`}</p>
</Modal>
<Modal
visible={renameHint}
onCancel={() => setHintShow(false)}
footer={[
<Button key='link' onClick={() => setRenameHint(false)}>
取消
</Button>,
<Button
key='back'
type='primary'
onClick={() => {
const oldRoute = tabList[renameIndex].route
if (!oldRoute) return
const flagStr = oldRoute?.indexOf("/") > -1 ? "/" : "\\"
const routes = oldRoute?.split(flagStr)
routes?.pop()
const newRoute = routes?.concat([renameCache]).join(flagStr)
if (!oldRoute || !newRoute) return
renameFile(renameIndex, renameCache, oldRoute, newRoute, () => {
setRenameHint(false)
})
}}
>
确定
</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}}>{`是否要覆盖已存在的文件吗?`}</p>
</Modal>
</div>
</AutoCard>
)
}
Example #12
Source File: YakScriptCreator.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
YakScriptCreatorForm: React.FC<YakScriptCreatorFormProp> = (props) => {
const [params, setParams] = useState<YakScript>(props.modified || {
Content: "", Tags: "", Author: "", Level: "",
IsHistory: false,
CreatedAt: 0,
Help: "",
Id: 0,
Params: [],
ScriptName: "",
Type: "yak",
IsGeneralModule: false,
PluginSelectorTypes: "mitm,port-scan"
});
const [paramsLoading, setParamsLoading] = useState(false);
const [modified, setModified] = useState<YakScript | undefined>(props.modified);
const [fullscreen, setFullscreen] = useState(false);
const [loading, setLoading] = useState(false);
const isNucleiPoC = params.Type === "nuclei";
const debugButton = (primary?: boolean) => {
if (loading) {
return <Button disabled={true}>执行中...无法调试</Button>
}
return
}
useEffect(() => {
if (paramsLoading) {
setTimeout(() => {
setParamsLoading(false)
}, 400)
}
}, [paramsLoading])
useEffect(() => {
switch (params.Type) {
case "mitm":
setParams({...params, Content: MITMPluginTemplate})
return
case "port-scan":
setParams(
{
...params, Content: PortScanPluginTemplate, Params: [
{
Field: "target",
FieldVerbose: "扫描的目标",
TypeVerbose: "string",
Required: true
} as YakScriptParam,
{
Field: "ports",
FieldVerbose: "端口",
TypeVerbose: "string",
Required: false,
DefaultValue: "80"
} as YakScriptParam,
]
},
)
return
case "packet-hack":
setParams({
...params, Content: PacketHackPluginTemplate, Params: [
{Field: "request", TypeVerbose: "http-packet", Required: true} as YakScriptParam,
{Field: "response", TypeVerbose: "http-packet", Required: false} as YakScriptParam,
{Field: "isHttps", TypeVerbose: "bool",} as YakScriptParam,
]
})
return
case "codec":
setParams({...params, Content: CodecPluginTemplate})
return
case "nuclei":
setParams({...params, Content: "# Add your nuclei formatted PoC!"})
return
default:
setParams({...params, Content: "yakit.AutoInitYakit()\n\n# Input your code!\n\n"})
return
}
}, [params.Type])
useEffect(() => {
if (props.modified) setParams({...props.modified});
}, [props.modified])
return <div>
<Form
onSubmitCapture={e => {
e.preventDefault()
ipcRenderer.invoke("SaveYakScript", params).then((data) => {
info("创建 / 保存 Yak 脚本成功")
props.onCreated && props.onCreated(data)
props.onChanged && props.onChanged(data)
setTimeout(() => ipcRenderer.invoke("change-main-menu"), 100);
}).catch((e: any) => {
failed(`保存 Yak 模块失败: ${e}`)
})
}}
labelCol={{span: 5}} wrapperCol={{span: 14}}
>
<SelectOne disabled={!!modified} label={"模块类型"}
data={[
{value: "yak", text: "Yak 原生模块"},
{value: "mitm", text: "MITM 模块"},
{value: "packet-hack", text: "Packet 检查"},
{value: "port-scan", text: "端口扫描插件"},
{value: "codec", text: "Codec 模块"},
{value: "nuclei", text: "nuclei Yaml模块"},
]}
setValue={Type => {
if (["packet-hack", "codec", "nuclei"].includes(Type)) setParams({
...params,
Type,
IsGeneralModule: false
})
else setParams({...params, Type})
}} value={params.Type}
/>
<InputItem
label={"Yak 模块名"} required={true}
setValue={ScriptName => setParams({...params, ScriptName})} value={params.ScriptName}
/>
<InputItem
label={"简要描述"}
setValue={Help => setParams({...params, Help})} value={params.Help}
/>
<InputItem
label={"模块作者"} setValue={Author => setParams({...params, Author})} value={params.Author}
/>
<ManyMultiSelectForString
label={"Tags"}
data={[
{value: "教程", label: "教程"}
]} mode={"tags"}
setValue={Tags => setParams({...params, Tags})} value={params.Tags}
/>
{["yak", "mitm"].includes(params.Type) && <Form.Item label={"增加参数"}>
<Button type={"link"}
onClick={() => {
let m = showModal({
title: "添加新参数",
width: "60%",
content: <>
<CreateYakScriptParamForm onCreated={param => {
let flag = false
const paramArr = (params.Params || []).map(item => {
if (item.Field === param.Field) {
flag = true
info(`参数 [${param.Field}]${param.FieldVerbose ? `(${param.FieldVerbose})` : ""} 已经存在,已覆盖旧参数`)
return param
}
return item
})
if (!flag) paramArr.push(param)
setParams({...params, Params: [...paramArr]})
m.destroy()
}}/>
</>
})
}}
>添加 / 设置一个参数 <PlusOutlined/></Button>
</Form.Item>}
{params.Params.length > 0 ? <Form.Item label={" "} colon={false}>
<List
size={"small"} bordered={true} pagination={false}
renderItem={p => {
return <List.Item key={p.Field}>
<Space size={1}>
{p.Required && <div className="form-item-required-title">*</div>}
参数名:
</Space>
<Tag
color={"geekblue"}>{p.FieldVerbose && `${p.FieldVerbose} / `}{p.Field}
</Tag>
类型:<Tag color={"blue"}>{p.TypeVerbose} {p.DefaultValue && `默认值:${p.DefaultValue}`}</Tag>
{p.DefaultValue && `默认值为: ${p.DefaultValue}`}
{!isNucleiPoC && <Space style={{marginLeft: 20}}>
<Button size={"small"} onClick={() => {
let m = showModal({
title: `修改已知参数: ${p.FieldVerbose}(${p.Field})`,
width: "60%",
content: <>
<CreateYakScriptParamForm
modifiedParam={p}
onCreated={param => {
setParams({
...params, Params: [
...params.Params.filter(i => i.Field !== param.Field),
param,
]
})
m.destroy()
}}/>
</>
})
}}>修改参数</Button>
<Popconfirm
title={"确认要删除该参数吗?"}
onConfirm={e => {
setParamsLoading(true)
setParams({...params, Params: params.Params.filter(i => i.Field !== p.Field)})
}}
>
<Button size={"small"} type={"link"} danger={true}>删除参数</Button>
</Popconfirm>
</Space>}
</List.Item>
}}
dataSource={params.Params}
>
</List>
</Form.Item> : ""}
{params.Type === "yak" && <>
<SwitchItem
label={"启用插件联动 UI"} value={params.EnablePluginSelector} formItemStyle={{marginBottom: 2}}
setValue={EnablePluginSelector => setParams({...params, EnablePluginSelector})}
/>
{params.EnablePluginSelector && <ManyMultiSelectForString
label={"联动插件类型"} value={params.PluginSelectorTypes}
data={["mitm", "port-scan"].map(i => {
return {value: i, label: getPluginTypeVerbose(i)}
})} mode={"multiple"}
setValue={res => {
setParams({...params, PluginSelectorTypes: res})
}}
help={"通过 cli.String(`yakit-plugin-file`) 获取用户选择的插件"}
/>}
</>}
<Form.Item label={"源码"} help={<>
<Space>
<Button icon={<FullscreenOutlined/>}
onClick={() => {
setFullscreen(true)
let m = showDrawer({
title: "Edit Code",
width: "100%",
closable: false,
keyboard: false,
content: <>
<YakScriptLargeEditor
script={params}
onExit={(data) => {
m.destroy()
setFullscreen(false)
ipcRenderer.invoke("QueryYakScript", {})
}}
onUpdate={(data: YakScript) => {
props.onChanged && props.onChanged(data)
setParams({...data})
}}
/>
</>
})
}}
type={"link"} style={{
marginBottom: 12, marginTop: 6
}}>大屏模式</Button>
{!["packet-hack", "codec", "nuclei"].includes(params.Type) && <Checkbox
name={"默认启动"}
style={{
marginBottom: 12, marginTop: 6
}}
checked={params.IsGeneralModule}
onChange={() => setParams({
...params,
IsGeneralModule: !params.IsGeneralModule
})}>
默认启动 <Tooltip
title={"设置默认启动后,将在恰当时候启动该插件(Yak插件不会自动启动,但会自动增加在左侧基础安全工具菜单栏)"}
>
<Button type={"link"} icon={<QuestionCircleOutlined/>}/>
</Tooltip>
</Checkbox>}
</Space>
</>}>
{!fullscreen && <div style={{height: 400}}>
<YakEditor
type={"yak"}
setValue={Content => setParams({...params, Content})}
value={params.Content}
/>
</div>}
</Form.Item>
<Form.Item colon={false} label={" "}>
<Space>
<Button type="primary" htmlType="submit"> {modified ? "修改当前" : "创建新的"} Yak 模块 </Button>
<Button
// type={primary ? "primary" : undefined}
disabled={[
// "mitm",
"",
].includes(params.Type)}
onClick={() => {
setLoading(true)
ipcRenderer.invoke("SaveYakScript", params).then((data: YakScript) => {
info("创建 / 保存 Yak 脚本成功")
setModified(data)
setParams(data)
props.onChanged && props.onChanged(data)
// YakScriptParamsSetter
executeYakScriptByParams(data)
}).catch((e: any) => {
failed(`保存 Yak 模块失败: ${e}`)
}).finally(() => {
setTimeout(() => setLoading(false), 400)
})
}}> 调试:创建(修改)并立即执行 </Button>
</Space>
</Form.Item>
</Form>
</div>
}
Example #13
Source File: editors.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
HTTPPacketEditor: React.FC<HTTPPacketEditorProp> = React.memo((props) => {
const isResponse = props.isResponse || (new Buffer(props.originValue.subarray(0, 5)).toString("utf8")).startsWith("HTTP/")
const [mode, setMode] = useState("text");
const [strValue, setStrValue] = useState(new Buffer(props.originValue).toString('utf8'));
const [hexValue, setHexValue] = useState<Uint8Array>(new Buffer(props.originValue))
const [searchValue, setSearchValue] = useState("");
const [monacoEditor, setMonacoEditor] = useState<IMonacoEditor>();
const [fontSize, setFontSize] = useState(12);
const [highlightDecorations, setHighlightDecorations] = useState<any[]>([]);
const [noWordwrap, setNoWordwrap] = useState(false);
const highlightActive = useMemoizedFn((search: string, regexp?: boolean) => {
if (!monacoEditor) {
return
}
// @ts-ignore
// let range = monacoEditor?.getModel().findMatches(search, false, !!regexp, false, null, false)
// if (range && range.length > 0) {
// const decs = monacoEditor.deltaDecorations(highlightDecorations, range.map(i => {
// return {
// id: `highlight[${searchValue}]`,
// range: i.range,
// options: {
// isWholeLine: false,
// inlineClassName: 'monacoInlineHighlight'
// }
// } as any
// }))
// setHighlightDecorations(decs)
// }
})
/*如何实现 monaco editor 高亮?*/
// https://microsoft.github.io/monaco-editor/playground.html#interacting-with-the-editor-line-and-inline-decorations
// hex editor
const [nonce, setNonce] = useState(0);
// The callback facilitates updates to the source data.
const handleSetValue = React.useCallback((offset, value) => {
hexValue[offset] = value;
setNonce(v => (v + 1));
setHexValue(new Buffer(hexValue))
}, [hexValue]);
useEffect(() => {
if (!props.defaultHeight) {
return
}
setStrValue(props.defaultStringValue || "")
setHexValue(Buffer.from(props.defaultStringValue || ""))
}, [props.defaultStringValue])
useEffect(() => {
if (monacoEditor) {
props.onEditor && props.onEditor(monacoEditor)
monacoEditor.setSelection({startColumn: 0, startLineNumber: 0, endLineNumber: 0, endColumn: 0})
}
if (!props.simpleMode && !props.hideSearch && monacoEditor) {
setHighlightDecorations(monacoEditor.deltaDecorations(highlightDecorations, []))
}
}, [monacoEditor])
useEffect(() => {
if (props.readOnly) {
setStrValue(new Buffer(props.originValue).toString('utf8'))
setHexValue(new Buffer(props.originValue))
}
if (props.readOnly && monacoEditor) {
monacoEditor.setSelection({startColumn: 0, startLineNumber: 0, endLineNumber: 0, endColumn: 0})
}
}, [
props.originValue,
props.readOnly,
// monacoEditor,
])
useEffect(() => {
if (props.readOnly) {
return
}
setStrValue(new Buffer(props.originValue).toString('utf8'))
setHexValue(new Buffer(props.originValue))
}, [props.refreshTrigger])
useEffect(() => {
props.onChange && props.onChange(Buffer.from(strValue))
}, [strValue])
useEffect(() => {
props.onChange && props.onChange(hexValue)
}, [hexValue])
const empty = !!props.emptyOr && props.originValue.length == 0
return <div style={{width: "100%", height: "100%"}}>
<Card
className={"flex-card"}
size={"small"} loading={props.loading}
bordered={props.bordered}
style={{height: "100%", width: "100%"}}
title={!props.noHeader && <Space>
{!props.noTitle && <span>{isResponse ? "Response" : "Request"}</span>}
{!props.simpleMode ? (!props.noHex && <SelectOne
label={" "}
colon={false} value={mode}
setValue={e => {
if (mode === "text" && e === "hex") {
setHexValue(new Buffer(strValue))
}
if (mode === "hex" && e === "text") {
setStrValue(Buffer.from(hexValue).toString("utf8"))
}
setMode(e)
}}
data={[
{text: "TEXT", value: "text"},
{text: "HEX", value: "hex"},
]} size={"small"} formItemStyle={{marginBottom: 0}}
/>) : <Form.Item style={{marginBottom: 0}}>
<Tag color={"geekblue"}>{mode.toUpperCase()}</Tag>
</Form.Item>}
{mode === "text" && !props.hideSearch && !props.simpleMode && <Input.Search
size={"small"} value={searchValue}
onChange={e => {
setSearchValue(e.target.value)
}} enterButton={true}
onSearch={e => {
highlightActive(searchValue)
}}
/>}
</Space>}
bodyStyle={{padding: 0, width: "100%", display: "flex", flexDirection: "column"}}
extra={!props.noHeader && <Space size={2}>
{props.extra}
{props.sendToWebFuzzer && <Button
size={"small"}
type={"primary"}
icon={<ThunderboltFilled/>}
onClick={() => {
ipcRenderer.invoke("send-to-tab", {
type: "fuzzer",
data: {isHttps: props.defaultHttps || false, request: strValue}
})
}}
>FUZZ</Button>}
<Tooltip title={"不自动换行"}>
<Button
size={"small"}
type={noWordwrap ? "link" : "primary"}
icon={<EnterOutlined/>}
onClick={() => {
setNoWordwrap(!noWordwrap)
}}
/>
</Tooltip>
{!props.simpleMode && <Popover
title={"配置编辑器"}
content={<>
<Form
onSubmitCapture={e => {
e.preventDefault()
}} size={"small"}
layout={"horizontal"}
wrapperCol={{span: 16}}
labelCol={{span: 8}}
>
<SelectOne
formItemStyle={{marginBottom: 4}}
label={"字号"}
data={[
{text: "小", value: 12},
{text: "中", value: 16},
{text: "大", value: 20},
]} value={fontSize} setValue={setFontSize}
/>
<Form.Item label={"全屏"}>
<Button
size={"small"}
type={"link"}
icon={<FullscreenOutlined/>}
onClick={() => {
showDrawer({
title: "全屏", width: "100%",
content: <div style={{height: "100%", width: "100%"}}>
<HTTPPacketEditor
{...props} disableFullscreen={true}
defaultHeight={670}
/>
</div>
})
}}
/>
</Form.Item>
</Form>
</>}
>
<Button
icon={<SettingOutlined/>}
type={"link"} size={"small"}
/>
</Popover>}
</Space>}
>
<div style={{flex: 1}}>
{empty && props.emptyOr}
{mode === "text" && !empty && <YakEditor
loading={props.loading}
type={props.language || (isResponse ? "html" : "http")}
value={
props.readOnly && props.originValue.length > 0 ?
new Buffer(props.originValue).toString() : strValue
}
readOnly={props.readOnly}
setValue={setStrValue} noWordWrap={noWordwrap}
fontSize={fontSize}
actions={[
...(props.actions || []),
...MonacoEditorCodecActions,
...(props.noPacketModifier ? [] : MonacoEditorMutateHTTPRequestActions),
...(props.noPacketModifier ? [] : MonacoEditorFullCodecActions),
]}
editorDidMount={editor => {
setMonacoEditor(editor)
}}
{...props.extraEditorProps}
/>}
{mode === "hex" && !empty && <HexEditor
className={props.system === 'Windows_NT' ? 'hex-editor-style' : ''}
showAscii={true}
data={hexValue}
showRowLabels={true}
showColumnLabels={false}
nonce={nonce}
onSetValue={props.readOnly ? undefined : handleSetValue}
/>}
</div>
</Card>
</div>
})