@ant-design/icons#CaretRightOutlined TypeScript Examples
The following examples show how to use
@ant-design/icons#CaretRightOutlined.
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: controlBar.tsx From covid_dashboard with MIT License | 6 votes |
public render() {
const { env, onChangeDate, onChangeSpeed } = this.props;
const { x3 } = this.state;
return (
<div className='controlbar'>
<img src={require('./images/bg.png')} />
{
env.speed > 0 ? (
<PauseOutlined className='playpause' onClick={() => onChangeSpeed(0)} />
) : (
<CaretRightOutlined className='playpause' onClick={this.handlePlay} style={{marginLeft: '3px'}} />
)
}
<StepBackwardOutlined className='start' onClick={() => onChangeDate(this.begin)} />
<StepForwardOutlined className='end' onClick={() => onChangeDate(this.end)}/>
<div className='speed' style={x3 ? {backgroundColor: '#020E26bb', color: 'white'} : undefined} onClick={this.handleX3}>x 3</div>
</div>
)
}
Example #2
Source File: PluginsEmptyState.tsx From posthog-foss with MIT License | 6 votes |
export function PluginsEmptyState(): JSX.Element {
const { setPluginTab } = useActions(pluginsLogic)
const { loading } = useValues(pluginsLogic)
const { user } = useValues(userLogic)
return (
<>
{loading ? (
<>
<Subtitle
subtitle={
<>
{' '}
<CaretRightOutlined /> {'Enabled plugins'}{' '}
</>
}
buttons={<Skeleton.Button style={{ width: 150 }} />}
/>
<PluginLoading />
</>
) : (
<>
<Subtitle subtitle="Installed plugins" />
<Row gutter={16} style={{ marginTop: 16 }}>
<Col span={24}>
<Empty description={<span>You haven't installed any plugins yet</span>}>
{canGloballyManagePlugins(user?.organization) && (
<Button type="default" onClick={() => setPluginTab(PluginTab.Repository)}>
Open the Plugin Repository
</Button>
)}
</Empty>
</Col>
</Row>
</>
)}
</>
)
}
Example #3
Source File: download-item.tsx From electron-playground with MIT License | 5 votes |
DownloadItem = ({
item,
index,
onOpenFile,
onPauseOrResume,
onOpenFolder,
onCancel,
}: DownloadProps) => {
return (
<div className={styles['download-item-container']} key={item.id}>
{/* 下载进度 */}
{item.state === 'progressing' && (
<div
className={styles['download-item-progress']}
style={{ width: `${item.progress * 100}%` }}
/>
)}
<div className={styles['download-item-main']}>
{/* 下载项的图标 */}
<div className={styles['file-icon']} onDoubleClick={() => onOpenFile?.(item.path)}>
<img src={item.icon} />
</div>
{/* 文件名、下载大小、速度 */}
<div className={styles['file-info']}>
<Tooltip title={item.fileName}>
<p className={styles['file-name']}>{item.fileName}</p>
</Tooltip>
<div className={styles['file-desc']}>
{item.state === 'progressing' ? (
<>
<div className={styles['file-size']}>
{getFileSize(item.receivedBytes, false)}/{getFileSize(item.totalBytes)}
</div>
<span className={styles['download-speed']}>{getFileSize(item.speed)}/s</span>
</>
) : null}
{item.state === 'completed' && <p>{getFileSize(item.totalBytes)}</p>}
</div>
</div>
{/* 操作 */}
<div className={styles.operating}>
{item.state === 'progressing' && (
<IconButton
title={item.paused ? '恢复' : '暂停'}
className={styles['operating-item']}
onClick={() => onPauseOrResume?.(item)}>
{item.paused ? <CaretRightOutlined /> : <PauseOutlined />}
</IconButton>
)}
{item.state === 'completed' && (
<IconButton
title='打开所在位置'
className={styles['operating-item']}
onClick={() => onOpenFolder?.(item.path)}>
<FolderViewOutlined />
</IconButton>
)}
<IconButton
title={`${item.state === 'progressing' ? '取消并' : ''}移除下载`}
className={styles['operating-item']}
onClick={() => onCancel?.(item, index)}>
<CloseOutlined />
</IconButton>
</div>
</div>
</div>
)
}
Example #4
Source File: SourceEditorFormStreamsLoadableForm.tsx From jitsu with MIT License | 5 votes |
StreamsCollapsibleList: React.FC<StreamsCollapsibleListProps> = React.memo(
({ streamsToDisplay, initiallySelectedStreams, isAllStreamsChecked, handleToggleStream, setSourceEditorState }) => {
/**
* Creates source type specific methods and components
*/
const getStreamUiComponents = (streamData: StreamData) => {
if (sourceEditorUtils.isAirbyteStream(streamData)) {
const handleChangeStreamSyncMode = (mode: string, stream: AirbyteStreamData): void => {
const newStream = { ...stream }
newStream.sync_mode = mode
updateStream(setSourceEditorState, SELECTED_STREAMS_SOURCE_DATA_PATH, newStream)
}
return {
header: (
<AirbyteStreamHeader streamName={streamData.stream.name} streamNamespace={streamData.stream.namespace} />
),
content: (
<AirbyteStreamParameters
streamData={streamData}
checked={isAllStreamsChecked}
handleChangeStreamSyncMode={handleChangeStreamSyncMode}
/>
),
}
} else if (sourceEditorUtils.isSingerStream(streamData)) {
return {
header: <SingerStreamHeader streamUid={streamData.tap_stream_id} streamName={streamData.stream} />,
content: <SingerStreamParameters streamData={streamData} />,
}
}
}
return (
<Collapse
expandIconPosition="left"
destroyInactivePanel
expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}
>
{streamsToDisplay
.sort((a, b) => {
// moves initially selected streams to the top of the list
const [aUid, bUid] = [a, b].map(sourceEditorUtils.getStreamUid)
const [aIsInitiallySelected, bIsInitiallySelected] = [aUid, bUid].map(uid =>
initiallySelectedStreams.some(selected => sourceEditorUtils.getSelectedStreamUid(selected) === uid)
)
return Number(bIsInitiallySelected) - Number(aIsInitiallySelected)
})
.map(streamData => {
const streamUid = sourceEditorUtils.getStreamUid(streamData)
const { header, content } = getStreamUiComponents(streamData)
return (
<StreamPanel
key={streamUid}
streamUid={streamUid}
header={header}
initiallySelectedStreams={initiallySelectedStreams}
checked={isAllStreamsChecked}
handleToggleStream={handleToggleStream}
>
{content}
</StreamPanel>
)
})}
</Collapse>
)
}
)
Example #5
Source File: FormDataItem.tsx From yugong with MIT License | 5 votes |
FormDataItem: React.FC<Props> = ({ onMinus, value, order }) => {
const { onChangeRunningData, runningData, dataPath } = useContext(FormModuleContext)
const onChange = useCallback(
(data: {[keys: string]: any}) => {
if (!runningData) return;
const operateData = cloneDeep(runningData)
const itemPath = `${dataPath}[${order - 1}]`;
const itemData = get(operateData, itemPath);
const newItemData = {
...itemData,
...data
}
set(operateData, itemPath, newItemData);
onChangeRunningData?.(operateData)
},
[dataPath, onChangeRunningData, order, runningData],
)
const [showOptions, setShowOptions] = useState(false);
const disabled = false;
return (
<div className={s.root}>
<LineItem
label={
<div className={s.dragwrap}>
<span className={s.drag}>
<DragHandle />
</span>
第{order}项
</div>
}
>
<Input
disabled={disabled}
className={s.inp}
onChange={(e) => onChange({title: e.target.value})}
value={value.title}
placeholder="名称"
/>
<Input name="rowMap"
disabled={disabled}
className={classNames(s.inp, s.nbl, s.nbrad)}
onChange={(e) => onChange({dataIndex: e.target.value})}
value={value?.dataIndex}
placeholder="字段(必填)"
/>
<Button
disabled={disabled}
className={classNames(s.btn, s.nbl, s.nbr)}
icon={showOptions ? <CaretDownOutlined /> : <CaretRightOutlined />}
onClick={() => setShowOptions(!showOptions)}
/>
<Button
disabled={disabled}
className={s.btn}
icon={<MinusOutlined />}
onClick={() => onMinus?.()}
/>
</LineItem>
<div style={{ display: showOptions ? 'block' : 'none' }}>
<LineItem label="">
<SubItem value={value} onChange={onChange} />
</LineItem>
</div>
</div>
);
}
Example #6
Source File: index.tsx From electron-playground with MIT License | 5 votes |
iconMap.set('run', { icon: <CaretRightOutlined />, text: '试一试' })
Example #7
Source File: collapse.tsx From XFlow with MIT License | 5 votes |
CollapseList: React.FC<ISearchList> = props => {
const {
onActiveKeyChange,
collapseData,
prefixClz,
onMouseDown,
modelService,
commandService,
graphConfig,
} = props
const renderHeader = (item: ICollapsePanel) => {
const { header, extra, icon, isCollapsed } = item
const onClick = (e: React.MouseEvent) => {
e.preventDefault()
onActiveKeyChange(item.id)
}
return (
<div className={`xflow-collapse-header`} onClick={onClick}>
<div className={`xflow-collapse-header-icon`}>
{isReactComponent(icon) ? (
React.createElement(icon, { isCollapsed })
) : (
<CaretRightOutlined rotate={isCollapsed ? 0 : 90} style={{ fontSize: '12px' }} />
)}
</div>
<div className={`xflow-collapse-header-label`}>
{isReactComponent(header) ? React.createElement(header, item) : header}
</div>
<div className={`xflow-collapse-header-extra`}>
{isReactComponent(extra) ? React.createElement(extra, item) : extra}
</div>
</div>
)
}
const renderChildren = (children: IPanelNode[]) => {
return (
<div className={`xflow-collapse-content`}>
{children.map(item => {
return (
<div
className={`xflow-collapse-content-item ${item.isDisabled ? 'disabled' : ''}`}
key={item.id}
>
<PanelNode
item={item}
onMouseDown={onMouseDown(item)}
popoverContent={item.popoverContent}
prefixClz={prefixClz}
modelService={modelService}
commandService={commandService}
graphConfig={graphConfig}
/>
</div>
)
})}
</div>
)
}
return (
<ul className="xflow-collapse-list">
{collapseData.length === 0 && <Empty style={{ marginTop: '24px' }} />}
{collapseData.map(collapseItem => {
const { children = [], isCollapsed, render } = collapseItem
const clz = isCollapsed ? 'close' : 'open'
return (
<li className={`xflow-collapse-list-item ${clz}`} key={collapseItem.id}>
{renderHeader(collapseItem)}
{isReactComponent(render)
? React.createElement(render, collapseItem)
: renderChildren(children)}
</li>
)
})}
</ul>
)
}
Example #8
Source File: editor-operate.tsx From electron-playground with MIT License | 5 votes |
export default function EditorOperate({ handleExec, setLayout, layout }: IProp): ReactElement {
const [exec, setExec] = useState<'RUN' | 'STOP'>('RUN')
const childRef = useRef<ChildProcess>()
const wrapExec = async () => {
if (childRef.current) {
childRef.current.kill()
// @ts-ignore
childRef.current = null
} else {
const child = await handleExec()
// @ts-ignore
childRef.current = child
setExec('STOP')
child?.on('close', code => {
setExec('RUN')
})
}
}
return (
<div className={styles.wrap}>
<div className={styles.title}>
<Button disabled size='small' icon={<FileTextOutlined />}>
Electron v{process.versions.electron}
</Button>
<Button
size='small'
onClick={wrapExec}
icon={exec === 'RUN' ? <CaretRightOutlined /> : <LoadingOutlined />}>
{exec}
</Button>
</div>
<div className='editors'>
<Dropdown overlay={<WrapMenu layout={layout} setLayout={setLayout} />}>
<Button size='small' icon={<FileTextOutlined />}>
Editors
</Button>
</Dropdown>
</div>
</div>
)
}
Example #9
Source File: UpgradeSection.tsx From posthog-foss with MIT License | 5 votes |
export function UpgradeSection(): JSX.Element {
const { checkForUpdates, toggleSectionOpen } = useActions(pluginsLogic)
const { sectionsOpen } = useValues(pluginsLogic)
const { user } = useValues(userLogic)
const {
filteredPluginsNeedingUpdates,
pluginsNeedingUpdates,
checkingForUpdates,
installedPluginUrls,
updateStatus,
rearranging,
hasUpdatablePlugins,
} = useValues(pluginsLogic)
const upgradeButton = canInstallPlugins(user?.organization) && hasUpdatablePlugins && (
<Button
type="default"
icon={pluginsNeedingUpdates.length > 0 ? <SyncOutlined /> : <CloudDownloadOutlined />}
onClick={(e) => {
e.stopPropagation()
checkForUpdates(true)
}}
loading={checkingForUpdates}
>
{checkingForUpdates
? `Checking plugin ${Object.keys(updateStatus).length + 1} out of ${
Object.keys(installedPluginUrls).length
}`
: pluginsNeedingUpdates.length > 0
? 'Check again for updates'
: 'Check for updates'}
</Button>
)
return (
<>
<div
className="plugins-installed-tab-section-header"
onClick={() => toggleSectionOpen(PluginSection.Upgrade)}
>
<Subtitle
subtitle={
<>
{sectionsOpen.includes(PluginSection.Upgrade) ? (
<CaretDownOutlined />
) : (
<CaretRightOutlined />
)}
{` Plugins to update (${filteredPluginsNeedingUpdates.length})`}
</>
}
buttons={!rearranging && sectionsOpen.includes(PluginSection.Upgrade) && upgradeButton}
/>
</div>
{sectionsOpen.includes(PluginSection.Upgrade) ? (
<>
{pluginsNeedingUpdates.length > 0 ? (
<Row gutter={16} style={{ marginTop: 16 }}>
{filteredPluginsNeedingUpdates.length > 0 ? (
<>
{filteredPluginsNeedingUpdates.map((plugin) => (
<InstalledPlugin key={plugin.id} plugin={plugin} showUpdateButton />
))}
</>
) : (
<p style={{ margin: 10 }}>No plugins match your search.</p>
)}
</Row>
) : (
<p style={{ margin: 10 }}>All your plugins are up to date. Great work!</p>
)}
</>
) : null}
</>
)
}
Example #10
Source File: DisabledPluginsSection.tsx From posthog-foss with MIT License | 5 votes |
export function DisabledPluginSection(): JSX.Element {
const { filteredDisabledPlugins, sectionsOpen, disabledPlugins } = useValues(pluginsLogic)
const { toggleSectionOpen } = useActions(pluginsLogic)
if (disabledPlugins.length === 0) {
return <></>
}
return (
<>
<div
className="plugins-installed-tab-section-header"
onClick={() => toggleSectionOpen(PluginSection.Disabled)}
>
<Subtitle
subtitle={
<>
{sectionsOpen.includes(PluginSection.Disabled) ? (
<CaretDownOutlined />
) : (
<CaretRightOutlined />
)}
{` Installed plugins (${filteredDisabledPlugins.length})`}
</>
}
/>
</div>
{sectionsOpen.includes(PluginSection.Disabled) ? (
<>
{filteredDisabledPlugins.length > 0 ? (
<Row gutter={16} style={{ marginTop: 16 }}>
{filteredDisabledPlugins.map((plugin) => (
<InstalledPlugin key={plugin.id} plugin={plugin} />
))}
</Row>
) : (
<p style={{ margin: 10 }}>No plugins match your search.</p>
)}
</>
) : null}
</>
)
}
Example #11
Source File: timeline.tsx From covid_dashboard with MIT License | 5 votes |
public render() {
const { env } = this.props;
const { catchBars, hoverDate, hots } = this.state;
return (
<div className='timeline' ref={r => this._rightRoot = r}>
<div className='bg' />
<div className='play_btn' onClick={this.handleBtnClick}>
{
env.speed > 0 ? (
<PauseOutlined className='btn' />
) : (
<CaretRightOutlined className='btn' style={{marginLeft: '3px'}} />
)
}
</div>
<div className='timeline_right'>
{this.drawPointer()}
<div className='timeline_container'
ref={r => this._container = r}
onMouseDown={this.props.env.isMobile ? undefined : this.handleMouseDown}
onTouchStart={this.handleTouchStart}
onMouseMove={this.props.env.isMobile ? undefined : (e: React.MouseEvent) => this.handleMouseMove(e.movementX)}
onTouchMove={(e: React.TouchEvent) => {
if(e.touches.length) {
this.handleMouseMove(e.touches[0].clientX - this._lastTouchX)
this._lastTouchX = e.touches[0].clientX;
}}}
onWheel={this.handleMouseWheel}>
{this.drawLine()}
<div className='river_con'>
<River start={this._renderStartDate} end={this._rangeEndDate} dayWidth={this.dateWidth()} onLineClick={this.handleRiverLineClick}/>
</div>
<div className='events'>
{ catchBars }
</div>
</div>
</div>
<div className='hot_panel' ref={r => this._hotPanel = r} onMouseOver={this.handleHotOver} onMouseOut={this.handleHotOut}>
{hoverDate && hots && <div className='hot_arrow'/>}
{hoverDate && hots && this.drawHots()}
</div>
</div>
)
}
Example #12
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 #13
Source File: HackerPlugin.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
HackerPlugin: React.FC<HackerPluginProps> = React.memo((props) => {
const [token, setToken] = useState<string>(randomString(40))
const [loading, setLoading] = useState<boolean>(false)
const [lists, setLists, getLists] = useGetState<YakScript[]>([])
const [keyword, setKeyword] = useState<string>("")
const [limit, setLimit] = useState<number>(100)
const [total, setTotal] = useState<number>(0)
const [selected, setSelected] = useState<string[]>([])
const [indeterminate, setIndeterminate] = useState<boolean>(false)
const [checked, setChecked] = useState<boolean>(false)
const containerRef = useRef()
const wrapperRef = useRef()
const [list] = useVirtualList(getLists(), {
containerTarget: containerRef,
wrapperTarget: wrapperRef,
itemHeight: 40,
overscan: 20
})
const [vlistHeigth, setVListHeight] = useState(600)
const [execting, setExecting] = useState<boolean>(false)
const [infoState, {reset, setXtermRef}, xtermRef] = useHoldingIPCRStream(
`execute-packet-yak-script`,
"ExecutePacketYakScript",
token,
() => setExecting(false)
)
const search = useMemoizedFn(() => {
setLoading(true)
queryYakScriptList(
"packet-hack",
(data, total) => {
setTotal(total || 0)
setLists(data)
},
() => setTimeout(() => setLoading(false), 300),
limit,
undefined,
keyword
)
})
const selectYakScript = useMemoizedFn((info: YakScript) => {
setSelected([info.ScriptName])
// if (!selected.includes(info.ScriptName)) {
// setSelected([...selected, info.ScriptName])
// }
})
const unselectYakScript = useMemoizedFn((info: YakScript) => {
setSelected([])
// setSelected(selected.filter((i) => i !== info.ScriptName))
})
// useEffect(() => {
// const totalYakScript = lists.length
// const filterArr = lists.filter((item) => selected.indexOf(item.ScriptName) > -1)
// const IndeterminateFlag =
// (filterArr.length > 0 && filterArr.length < totalYakScript && selected.length !== 0) ||
// (filterArr.length === 0 && selected.length !== 0)
// const checkedFlag = filterArr.length === totalYakScript && selected.length !== 0
// setIndeterminate(IndeterminateFlag)
// setChecked(checkedFlag)
// }, [selected, lists])
const startScript = useMemoizedFn(() => {
if (selected.length === 0) {
failed("请选一个插件后在点击执行")
return
}
setExecting(true)
const params: ExecutePacketYakScriptProp = {
ScriptName: selected[0],
IsHttps: props.isHTTPS,
Request: props.request
}
if (!!props.response) params.Response = props.response
ipcRenderer
.invoke("ExecutePacketYakScript", params, token)
.then(() => {})
.catch((e) => {
failed(`Start Packet Checker Error: ${e}`)
setExecting(false)
})
})
const cancelScript = useMemoizedFn(() => {
ipcRenderer.invoke("cancel-ExecutePacketYakScript", token)
})
useEffect(() => {
search()
}, [])
const renderListItem = useMemoizedFn((info: YakScript) => {
return (
<div key={info.ScriptName} className='list-opt'>
<Checkbox
checked={selected.includes(info.ScriptName)}
onChange={(r) => {
if (r.target.checked) selectYakScript(info)
else unselectYakScript(info)
}}
>
<Space>
<Text style={{maxWidth: 270}} ellipsis={{tooltip: true}}>
{info.ScriptName}
</Text>
{info.Help && (
<Button
size={"small"}
type={"link"}
onClick={() => {
showModal({
width: "40%",
title: "Help",
content: <>{info.Help}</>
})
}}
icon={<QuestionCircleOutlined />}
/>
)}
</Space>
</Checkbox>
<div style={{flex: 1, textAlign: "right"}}>
{info.Author && (
<Tooltip title={info.Author}>
<Button size={"small"} type={"link"} icon={<UserOutlined />} />
</Tooltip>
)}
</div>
</div>
)
})
return (
<div className='mitm-exec-plugin'>
<div className='left-body'>
<AutoCard
size='small'
bordered={false}
title={"数据包扫描插件(暂只支持单选)"}
bodyStyle={{padding: "0 4px", overflowY: "hidden"}}
extra={
<Space>
{/* <Checkbox
indeterminate={indeterminate}
onChange={(r) => {
if (r.target.checked) {
const newSelected = [...lists.map((i) => i.ScriptName), ...selected]
setSelected(newSelected.filter((e, index) => newSelected.indexOf(e) === index))
} else {
setSelected([])
}
}}
checked={checked}
>
全选
</Checkbox> */}
<Popover
title={"额外设置"}
trigger={["click"]}
content={
<div>
<Form
size={"small"}
onSubmitCapture={(e) => {
e.preventDefault()
search()
}}
>
<InputInteger
label={"插件展示数量"}
value={limit}
setValue={setLimit}
formItemStyle={{marginBottom: 4}}
/>
<Form.Item colon={false} label={""} style={{marginBottom: 10}}>
<Button type='primary' htmlType='submit'>
刷新
</Button>
</Form.Item>
</Form>
</div>
}
>
<Button size={"small"} icon={<SettingOutlined />} type={"link"} />
</Popover>
<Popover
title={"搜索插件关键字"}
trigger={["click"]}
content={
<div>
<Form
size={"small"}
onSubmitCapture={(e) => {
e.preventDefault()
search()
}}
>
<InputItem
label={""}
extraFormItemProps={{style: {marginBottom: 4}, colon: false}}
value={keyword}
setValue={setKeyword}
/>
<Form.Item colon={false} label={""} style={{marginBottom: 10}}>
<Button type='primary' htmlType='submit'>
搜索
</Button>
</Form.Item>
</Form>
</div>
}
>
<Button
size={"small"}
type={!!keyword ? "primary" : "link"}
icon={<SearchOutlined />}
/>
</Popover>
{execting ? (
<Button
type='link'
danger
style={{padding: "4px 0"}}
icon={<PoweroffOutlined />}
onClick={cancelScript}
/>
) : (
<Button
type='link'
style={{padding: "4px 0"}}
icon={<CaretRightOutlined />}
onClick={() => {
xtermClear(xtermRef)
reset()
startScript()
}}
/>
)}
</Space>
}
>
<div style={{height: "100%"}}>
<ReactResizeDetector
onResize={(width, height) => {
if (!width || !height) {
return
}
setVListHeight(height)
}}
handleWidth={true}
handleHeight={true}
refreshMode={"debounce"}
refreshRate={50}
/>
<div ref={containerRef as any} style={{height: vlistHeigth, overflow: "auto"}}>
<div ref={wrapperRef as any}>{list.map((i) => renderListItem(i.data))}</div>
</div>
</div>
</AutoCard>
</div>
<div className='right-body'>
<AutoCard
size='small'
bordered={false}
title={
<Space>
{"已选插件 / 当页插件 / 插件总量"}
<Tag>{`${selected.length} / ${lists.length} / ${total}`}</Tag>
</Space>
}
bodyStyle={{padding: 0, paddingLeft: 5}}
>
<PluginResultUI
results={infoState.messageState}
progress={infoState.processState}
featureType={infoState.featureTypeState}
feature={infoState.featureMessageState}
statusCards={infoState.statusState}
loading={loading}
onXtermRef={setXtermRef}
/>
</AutoCard>
</div>
</div>
)
})
Example #14
Source File: Results.tsx From datart with Apache License 2.0 | 4 votes |
Results = memo(({ height = 0, width = 0 }: ResultsProps) => {
const { actions } = useViewSlice();
const dispatch = useDispatch();
const viewId = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'id' }),
) as string;
const model = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'model' }),
) as HierarchyModel;
const columnPermissions = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'columnPermissions' }),
) as ColumnPermission[];
const stage = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'stage' }),
) as ViewViewModelStages;
const previewResults = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'previewResults' }),
) as ViewViewModel['previewResults'];
const roles = useSelector(selectRoles);
const t = useI18NPrefix('view');
const dataSource = useMemo(
() => previewResults.map(o => ({ ...o, [ROW_KEY]: uuidv4() })),
[previewResults],
);
const modelChange = useCallback(
(columnName: string, column: Omit<Column, 'name'>) =>
({ key }) => {
let value;
if (key.includes('category')) {
const category = key.split('-')[1];
value = { ...column, category };
} else {
value = { ...column, type: key };
}
const clonedHierarchyModel = CloneValueDeep(model.hierarchy || {});
if (columnName in clonedHierarchyModel) {
clonedHierarchyModel[columnName] = value;
} else {
Object.values(clonedHierarchyModel)
.filter(col => !isEmptyArray(col.children))
.forEach(col => {
const targetChildColumnIndex = col.children!.findIndex(
child => child.name === columnName,
);
if (targetChildColumnIndex > -1) {
col.children![targetChildColumnIndex] = value;
}
});
}
dispatch(
actions.changeCurrentEditingView({
model: {
...model,
hierarchy: clonedHierarchyModel,
version: APP_CURRENT_VERSION,
},
}),
);
},
[dispatch, actions, model],
);
const roleDropdownData = useMemo(
() =>
roles.map(({ id, name }) => ({
key: id,
title: name,
value: id,
isLeaf: true,
})),
[roles],
);
const checkRoleColumnPermission = useCallback(
columnName => checkedKeys => {
const fullPermissions = Object.keys(model?.columns || {});
dispatch(
actions.changeCurrentEditingView({
columnPermissions: roleDropdownData.reduce<ColumnPermission[]>(
(updated, { key }) => {
const permission = columnPermissions.find(
({ subjectId }) => subjectId === key,
);
const checkOnCurrentRole = checkedKeys.includes(key);
if (permission) {
if (checkOnCurrentRole) {
const updatedColumnPermission = Array.from(
new Set(permission.columnPermission.concat(columnName)),
);
return fullPermissions.sort().join(',') !==
updatedColumnPermission.sort().join(',')
? updated.concat({
...permission,
columnPermission: updatedColumnPermission,
})
: updated;
} else {
return updated.concat({
...permission,
columnPermission: permission.columnPermission.filter(
c => c !== columnName,
),
});
}
} else {
return !checkOnCurrentRole
? updated.concat({
id: uuidv4(),
viewId,
subjectId: key,
subjectType: SubjectTypes.Role,
columnPermission: fullPermissions.filter(
c => c !== columnName,
),
})
: updated;
}
},
[],
),
}),
);
},
[dispatch, actions, viewId, model, columnPermissions, roleDropdownData],
);
const getExtraHeaderActions = useCallback(
(name: string, column: Omit<Column, 'name'>) => {
// 没有记录相当于对所有字段都有权限
const checkedKeys =
columnPermissions.length > 0
? roleDropdownData.reduce<string[]>((selected, { key }) => {
const permission = columnPermissions.find(
({ subjectId }) => subjectId === key,
);
if (permission) {
return permission.columnPermission.includes(name)
? selected.concat(key)
: selected;
} else {
return selected.concat(key);
}
}, [])
: roleDropdownData.map(({ key }) => key);
return [
<Popup
key={`${name}_columnpermission`}
trigger={['click']}
placement="bottomRight"
content={
<Tree
className="dropdown"
treeData={roleDropdownData}
checkedKeys={checkedKeys}
loading={false}
selectable={false}
onCheck={checkRoleColumnPermission(name)}
blockNode
checkable
/>
}
>
<Tooltip title={t('columnPermission.title')}>
<ToolbarButton
size="small"
iconSize={FONT_SIZE_BASE}
icon={
checkedKeys.length > 0 ? (
<EyeOutlined
className={classnames({
partial: checkedKeys.length !== roleDropdownData.length,
})}
/>
) : (
<EyeInvisibleOutlined />
)
}
/>
</Tooltip>
</Popup>,
];
},
[columnPermissions, roleDropdownData, checkRoleColumnPermission, t],
);
const pagination = useMemo(
() => ({
defaultPageSize: 100,
pageSizeOptions: ['100', '200', '500', '1000'],
}),
[],
);
return stage > ViewViewModelStages.Fresh ? (
<TableWrapper>
<SchemaTable
height={height ? height - 96 : 0}
width={width}
model={model.columns || {}}
hierarchy={model.hierarchy || {}}
dataSource={dataSource}
pagination={pagination}
getExtraHeaderActions={getExtraHeaderActions}
onSchemaTypeChange={modelChange}
hasCategory
/>
</TableWrapper>
) : (
<InitialDesc>
<p>
{t('resultEmpty1')}
<CaretRightOutlined />
{t('resultEmpty2')}
</p>
</InitialDesc>
);
})
Example #15
Source File: Toolbar.tsx From datart with Apache License 2.0 | 4 votes |
Toolbar = memo(({ allowManage, allowEnableViz }: ToolbarProps) => {
const { actions } = useViewSlice();
const dispatch = useDispatch();
const { onRun, onSave } = useContext(EditorContext);
const { showSaveForm } = useContext(SaveFormContext);
const sources = useSelector(selectSources);
const history = useHistory();
const histState = history.location.state as any;
const viewsData = useSelector(selectViews);
const t = useI18NPrefix('view.editor');
const saveAsView = useSaveAsView();
const startAnalysis = useStartAnalysis();
const id = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'id' }),
) as string;
const name = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'name' }),
) as string;
const parentId = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'parentId' }),
) as string;
const config = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'config' }),
) as object;
const sourceId = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'sourceId' }),
) as string;
const stage = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'stage' }),
) as ViewViewModelStages;
const status = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'status' }),
) as ViewStatus;
const script = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'script' }),
) as string;
const fragment = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'fragment' }),
) as string;
const size = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'size' }),
) as number;
const error = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'error' }),
) as string;
const ViewIndex = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'index' }),
) as number;
const isArchived = status === ViewStatus.Archived;
const formatSQL = useCallback(() => {
dispatch(
actions.changeCurrentEditingView({
script: format(script),
}),
);
}, [dispatch, actions, script]);
const showEdit = useCallback(() => {
showSaveForm({
type: CommonFormTypes.Edit,
visible: true,
initialValues: {
name,
parentId,
config,
},
parentIdLabel: t('folder'),
onSave: (values, onClose) => {
let index = ViewIndex;
if (isParentIdEqual(parentId, values.parentId)) {
index = getInsertedNodeIndex(values, viewsData);
}
dispatch(
actions.changeCurrentEditingView({
...values,
parentId: values.parentId || null,
index,
}),
);
dispatch(saveView({ resolve: onClose }));
},
});
}, [
showSaveForm,
actions,
dispatch,
name,
parentId,
config,
viewsData,
ViewIndex,
t,
]);
const sourceChange = useCallback(
value => {
dispatch(actions.changeCurrentEditingView({ sourceId: value }));
},
[dispatch, actions],
);
const sizeMenuClick = useCallback(
({ key }) => {
dispatch(actions.changeCurrentEditingView({ size: Number(key) }));
},
[dispatch, actions],
);
useEffect(() => {
if (histState?.sourcesId && sources) {
sourceChange(histState.sourcesId);
}
}, [histState?.sourcesId, sourceChange, sources]);
return (
<Container>
<Operates>
<Space split={<Divider type="vertical" className="divider" />}>
{allowManage && (
<Select
placeholder={t('source')}
value={sourceId}
bordered={false}
disabled={isArchived}
onChange={sourceChange}
className="source"
>
{sources.map(({ id, name }) => (
<Select.Option key={id} value={id}>
{name}
</Select.Option>
))}
</Select>
)}
<Space>
<Tooltip
title={
<TipTitle
title={[
`${fragment ? t('runSelection') : t('run')}`,
t('runWinTip'),
t('runMacTip'),
]}
/>
}
placement="bottom"
>
<ToolbarButton
icon={
stage === ViewViewModelStages.Running ? (
<PauseOutlined />
) : (
<CaretRightOutlined />
)
}
color={fragment ? WARNING : INFO}
onClick={onRun}
/>
</Tooltip>
<Tooltip title={t('beautify')} placement="bottom">
<ToolbarButton
icon={<AlignCenterOutlined />}
disabled={isArchived}
onClick={formatSQL}
/>
</Tooltip>
</Space>
<Dropdown
trigger={['click']}
overlay={
<Menu onClick={sizeMenuClick}>
{PREVIEW_SIZE_LIST.map(s => (
<Menu.Item key={s}>{s}</Menu.Item>
))}
</Menu>
}
>
<ToolbarButton size="small">{`Limit: ${size}`}</ToolbarButton>
</Dropdown>
<Chronograph
running={stage === ViewViewModelStages.Running}
status={
error
? 'error'
: stage >= ViewViewModelStages.Running
? stage === ViewViewModelStages.Running
? 'processing'
: 'success'
: 'default'
}
/>
</Space>
</Operates>
<Actions>
<Space>
{allowManage && (
<Tooltip
title={
<TipTitle
title={[t('save'), t('saveWinTip'), t('saveMacTip')]}
/>
}
placement="bottom"
>
<ToolbarButton
icon={<SaveFilled />}
disabled={isArchived || stage !== ViewViewModelStages.Saveable}
color={INFO}
onClick={onSave}
/>
</Tooltip>
)}
{allowManage && (
<Tooltip title={t('info')} placement="bottom">
<ToolbarButton
icon={<SettingFilled />}
disabled={isArchived || isNewView(id)}
color={INFO}
onClick={showEdit}
/>
</Tooltip>
)}
{allowManage && (
<Tooltip title={t('saveAs')} placement="bottom">
<ToolbarButton
icon={<CopyFilled />}
onClick={() => saveAsView(id)}
disabled={isNewView(id)}
color={INFO}
/>
</Tooltip>
)}
{/* <Tooltip title={t('saveFragment')} placement="bottom">
<ToolbarButton icon={<SnippetsFilled />} />
</Tooltip> */}
{allowEnableViz && (
<Tooltip title={t('startAnalysis')} placement="bottom">
<ToolbarButton
disabled={isNewView(id)}
icon={<MonitorOutlined />}
color={INFO}
onClick={() => {
startAnalysis(id);
}}
/>
</Tooltip>
)}
</Space>
</Actions>
</Container>
);
})
Example #16
Source File: ScheduleList.tsx From datart with Apache License 2.0 | 4 votes |
Operations: FC<OperationsProps> = ({
active,
scheduleId,
onUpdateScheduleList,
editingId,
}) => {
const [startLoading, setStartLoading] = useState(false);
const [stopLoading, setStopLoading] = useState(false);
const [executeLoading, setExecuteLoading] = useState(false);
const { actions } = useScheduleSlice();
const dispatch = useDispatch();
const t = useI18NPrefix('main.pages.schedulePage.sidebar.scheduleList');
const updateScheduleActive = useCallback(
(active: boolean) => {
dispatch(actions.setEditingScheduleActive(active));
},
[actions, dispatch],
);
const handleStartSchedule = useCallback(() => {
setStartLoading(true);
startSchedule(scheduleId)
.then(res => {
if (!!res) {
message.success(t('successStarted'));
onUpdateScheduleList();
if (editingId === scheduleId) {
updateScheduleActive(true);
}
}
})
.finally(() => {
setStartLoading(false);
});
}, [scheduleId, editingId, onUpdateScheduleList, updateScheduleActive, t]);
const handleStopSchedule = useCallback(() => {
setStopLoading(true);
stopSchedule(scheduleId)
.then(res => {
if (!!res) {
message.success(t('successStop'));
onUpdateScheduleList();
if (editingId === scheduleId) {
updateScheduleActive(false);
}
}
})
.finally(() => {
setStopLoading(false);
});
}, [scheduleId, onUpdateScheduleList, editingId, updateScheduleActive, t]);
const handleExecuteSchedule = useCallback(() => {
setExecuteLoading(true);
executeSchedule(scheduleId)
.then(res => {
if (!!res) {
message.success(t('successImmediately'));
onUpdateScheduleList();
}
})
.finally(() => {
setExecuteLoading(false);
});
}, [scheduleId, onUpdateScheduleList, t]);
return (
<Space onClick={stopPPG}>
{!active ? (
<OperationButton
tooltipTitle={t('start')}
loading={startLoading}
icon={<CaretRightOutlined />}
onClick={handleStartSchedule}
/>
) : null}
{active ? (
<OperationButton
tooltipTitle={t('stop')}
loading={stopLoading}
icon={<PauseOutlined />}
onClick={handleStopSchedule}
/>
) : null}
<Popconfirm
title={t('executeItNow')}
okButtonProps={{ loading: executeLoading }}
onConfirm={handleExecuteSchedule}
>
<OperationButton
loading={executeLoading}
tooltipTitle={t('executeImmediately')}
icon={
<SendOutlined
rotate={-45}
css={`
position: relative;
top: -2px;
`}
/>
}
/>
</Popconfirm>
</Space>
);
}
Example #17
Source File: ContextMenuComponent.tsx From ant-simple-draw with MIT License | 4 votes |
ContextMenu = memo(function ContextMenu(props) {
const dispatch = useDispatch();
const { editHandle } = useEdit();
const [curComponent, left, top, menuShow, componentDataList, copyData, zenMode] = useSelector(
createSelector(
[
(state: storeType) => state.component,
(state: storeType) => state.contextMenu,
(state: storeType) => state.copys,
(state: storeType) => state.config,
],
({ componentDataList, curComponent }, { left, top, menuShow }, { copyData }, { zenMode }) =>
[curComponent, left, top, menuShow, componentDataList, copyData, zenMode] as const,
),
);
const renderList = useMemo(() => {
const isClick = curComponent ? true : false;
let contextMenuList: contextMenuListType[] = [
{
title: '复制',
keyText: 'Ctrl+C',
icon: <CopyOutlined />,
isClick,
},
{
title: '粘贴',
keyText: 'Ctrl+V',
icon: <SnippetsOutlined />,
isClick: copyData ? true : false,
},
{
title: '剪切',
keyText: 'Ctrl+X',
icon: <ScissorOutlined />,
isClick,
},
{
title: '删除',
keyText: 'Delete',
icon: <DeleteOutlined />,
isClick,
},
{
title: `禅模式${zenMode ? '(关闭)' : '(开启)'}`,
keyText: 'Alt+Z',
icon: <DeleteOutlined />,
isClick: true,
},
{
title: '图层层级',
keyText: 'Combination' as any,
icon: <SvgIcon iconClass="layer" />,
isClick,
childrenIcon: <CaretRightOutlined />,
children: [
{
title: '移到顶层',
keyText: 'Ctrl+Shift+Up',
isClick,
},
{
title: '上移一层',
keyText: 'Ctrl+Up',
isClick,
},
{
title: '下移一层',
keyText: 'Ctrl+Down',
isClick,
},
{
title: '移到底层',
keyText: 'Ctrl+Shift+Down',
isClick,
},
],
},
{
title: '清屏',
keyText: 'Shift+A',
icon: <ClearOutlined />,
isClick: componentDataList.length ? true : false,
},
];
return contextMenuList;
}, [componentDataList, curComponent, copyData, zenMode]);
const menu = (e: React.MouseEvent, item: contextMenuListType) => {
const { keyText, isClick } = item;
e.preventDefault();
e.stopPropagation();
if (!isClick || item.children?.length) {
return;
}
editHandle(keyText, { isContextMenuMouse: true });
dispatch(hideContextMenuAction());
};
const ContextMenuItem = (data: contextMenuListType[]) => {
return data.map((item) => {
return (
<li
onClick={(e) => menu(e, item)}
key={item.keyText}
style={{
borderTop: item.keyText === 'Shift+A' ? '1px solid #0000000f' : 'none',
cursor: item.isClick === true ? 'pointer' : 'not-allowed',
color: item.isClick === true ? '#000000' : '#00000040',
}}
>
<div className={style.contextmenudes}>
{item.icon ? <span style={{ paddingRight: '8px' }}>{item.icon}</span> : null}
<span>{item.title}</span>
</div>
<div className={style.contextmenuHotkey}>
{item.childrenIcon ? item.childrenIcon : item.keyText}
</div>
{item.children ? <ul className={style.child}>{ContextMenuItem(item.children)}</ul> : null}
</li>
);
});
};
return (
<>
{menuShow ? (
<div
className={style.contextmenu}
style={{ top: top + 'px', left: left + 'px' }}
onMouseDown={(e) => {
// 不写这个的话,会触发组合器 onMousemove事件
e.preventDefault();
e.stopPropagation();
}}
>
<ul
onMouseUp={(e) => {
e.preventDefault();
e.stopPropagation();
// dispatch(isClickComponentAction(true));
}}
>
{ContextMenuItem(renderList)}
</ul>
</div>
) : null}
</>
);
})
Example #18
Source File: SourceEditorFormStreamsConfigurable.tsx From jitsu with MIT License | 4 votes |
SourceEditorFormStreamsConfigurable = ({
initialSourceData,
sourceDataFromCatalog,
setSourceEditorState,
}: Props) => {
const [selectedCollectionTypes, setSelectedCollectionTypes] = useState(sourceDataFromCatalog.collectionTypes)
const [addStreamVisible, setAddStreamVisible] = useState(false)
const [addTemplateVisible, setAddTemplateVisible] = useState(false)
const [activePanel, setActivePanel] = useState([])
const input = useRef(null)
const [form] = Form.useForm()
const renderAddButton = sourceDataFromCatalog.collectionTypes.length <= 1
const renderAddPopover = sourceDataFromCatalog.collectionTypes.length > 1
const renderApplyTemplates = sourceDataFromCatalog.collectionTemplates
const handleValuesChange = useDebouncedCallback(
(_, values: { [SELECTED_STREAMS_SOURCE_DATA_PATH]: UnknownObject[] }) => {
setSelectedStreams(
setSourceEditorState,
SELECTED_STREAMS_SOURCE_DATA_PATH,
values[SELECTED_STREAMS_SOURCE_DATA_PATH]
)
},
100
)
const handleCollectionTypesFilter = useCallback(
e => {
setSelectedCollectionTypes(
sourceDataFromCatalog.collectionTypes.filter(v => v.toLowerCase().includes(e.target.value.toLowerCase()))
)
},
[sourceDataFromCatalog]
)
const getStream = useCallback(
(index: number) => {
return form.getFieldsValue().collections?.[index] ?? initialSourceData.collections[index]
},
[initialSourceData.collections, form]
)
const getFormErrors = useCallback(
(index: number) => {
let fields = sourceDataFromCatalog.collectionParameters.map(v => ["collections", index, "parameters", v.id])
fields.push(["collections", index, "name"])
return form.getFieldsError(fields).filter(v => v.errors.length > 0)
},
[form, sourceDataFromCatalog]
)
const getCollectionParametersForType = useCallback(
(type: string) => {
return sourceDataFromCatalog.collectionParameters?.filter(
({ applyOnlyTo }: CollectionParameter) => !applyOnlyTo || applyOnlyTo === type
)
},
[sourceDataFromCatalog.collectionParameters]
)
const getCollectionParameters = useCallback(
(index: number) => {
return getCollectionParametersForType(getStream(index).type)
},
[getCollectionParametersForType, getStream]
)
const generateReportNameForType = useCallback(
(type: string) => {
const formValues = form.getFieldsValue()
const collections = formValues?.collections ?? [{}]
const blankName = type
const reportNames =
collections?.reduce((accumulator: string[], current: CollectionSource) => {
if (current?.name?.includes(blankName)) {
accumulator.push(current.name)
}
return accumulator
}, []) || []
return getUniqueAutoIncId(blankName, reportNames)
},
[form]
)
const addNewOfType = useCallback(
(type: string, operation: FormListOperation) => {
let newCollection = {
name: generateReportNameForType(type),
type: type,
parameters: {},
_id: randomId(),
}
for (const param of getCollectionParametersForType(type)) {
if (param.defaultValue) {
newCollection.parameters[param.id] = param.defaultValue
}
}
operation.add(newCollection, 0)
setActivePanel(activePanel.concat(newCollection._id))
},
[, sourceDataFromCatalog.collectionTemplates, activePanel, setActivePanel]
)
const remove = useCallback(
(index: number, operation: FormListOperation) => {
const stream = getStream(index)
const keyToRemove = stream._id ?? stream.name
operation.remove(index)
setActivePanel(activePanel.filter(v => v !== keyToRemove))
},
[, activePanel, setActivePanel, getStream]
)
const handleApplyTemplate = useCallback(
(chosenTemplate: number, operation: FormListOperation) => {
if (chosenTemplate >= 0) {
let newActivePanel = activePanel
let template = sourceDataFromCatalog.collectionTemplates[chosenTemplate]
for (const col of template.collections) {
let copy = JSON.parse(JSON.stringify(col))
if (copy.name) {
copy.name = generateReportNameForType(copy.name)
} else {
copy.name = generateReportNameForType(copy.type)
}
copy._id = randomId()
operation.add(copy, 0)
newActivePanel = newActivePanel.concat(copy._id)
}
setActivePanel(newActivePanel)
}
},
[sourceDataFromCatalog.collectionTemplates, activePanel, setActivePanel]
)
const handleTouchParameter = useCallback(
(index: number, e: ChangeEvent<HTMLInputElement>) => {
const formValues = form.getFieldsValue()
const collections = formValues.collections
const stream = collections[index]
if (typeof stream._id === "undefined") {
stream._id = input.current.state.value
}
form.setFieldsValue({ collections })
},
[form, , activePanel, setActivePanel, input]
)
/**
* Pass form validator to the parent component
*/
useEffect(() => {
const validateConfigAndCountErrors = async (): Promise<number> => {
let errorsCount = 0
try {
await form.validateFields()
} catch (error) {
errorsCount = +error?.errorFields?.length
}
return errorsCount
}
setSourceEditorState(state => {
const newState: SourceEditorState = { ...state, streams: { ...state.streams } }
newState.streams.validateGetErrorsCount = validateConfigAndCountErrors
return newState
})
}, [])
return (
<Form
name="source-collections"
form={form}
initialValues={initialSourceData}
autoComplete="off"
onValuesChange={handleValuesChange}
>
<Form.List name={SELECTED_STREAMS_SOURCE_DATA_PATH}>
{(fields: FormListFieldData[], operation: FormListOperation, meta) => (
<>
<Row className={"pb-3"}>
<Col>
{renderAddButton && (
<Button
size="large"
className="mr-4"
onClick={() => addNewOfType(sourceDataFromCatalog.collectionTypes[0] ?? "default", operation)}
icon={<PlusOutlined />}
>
Add new stream
</Button>
)}
{renderAddPopover && (
<Popover
placement="rightTop"
visible={addStreamVisible}
onVisibleChange={setAddStreamVisible}
content={
<>
{sourceDataFromCatalog.collectionTypes.length > 7 && (
<Input
allowClear={true}
onChange={handleCollectionTypesFilter}
placeholder={"Type to search"}
/>
)}
<div className={styles.templates} style={{ maxHeight: "400px" }}>
{selectedCollectionTypes.map((type: string) => (
<div key={type} className={styles.template}>
<div
onClick={() => {
addNewOfType(type, operation)
setAddStreamVisible(false)
}}
>
<p className="font-bold">{type}</p>
</div>
<Button
className={styles.button}
type="primary"
onClick={() => {
addNewOfType(type, operation)
setAddStreamVisible(false)
}}
>
Add
</Button>
</div>
))}
</div>
</>
}
trigger="click"
>
<Button size="large" className="mr-4" icon={<PlusOutlined />}>
Add new stream
</Button>
</Popover>
)}
{renderApplyTemplates && (
<>
<Popover
placement="rightTop"
visible={addTemplateVisible}
onVisibleChange={setAddTemplateVisible}
content={
<div className={styles.templates}>
{sourceDataFromCatalog.collectionTemplates.map((template: CollectionTemplate, index) => (
<div key={template.templateName} className={styles.template}>
<div>
<p className="font-bold capitalize">{template.templateName}</p>
{template.description && <p className={styles.comment}>{template.description}</p>}
<p>
Streams:{" "}
<span className={styles.comment}>
{template.collections
.map<React.ReactNode>(s => <>{s.name ?? s.type}</>)
.reduce((prev, curr) => [prev, ", ", curr])}
</span>
</p>
</div>
<Button
type="primary"
className={styles.button}
onClick={() => {
handleApplyTemplate(index, operation)
setAddTemplateVisible(false)
}}
>
Apply
</Button>
</div>
))}
</div>
}
trigger="click"
>
<Button className="mr-4" size={"large"}>
Use template
</Button>
</Popover>
</>
)}
</Col>
</Row>
<Collapse
activeKey={activePanel}
onChange={v => setActivePanel(v as string[])}
expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}
>
{fields.map((field: FormListFieldData) => {
return (
<Panel
key={getStream(field.name)._id ?? getStream(field.name).name}
header={
<div className={"flex items-center w-full flex-wrap lg:flex-nowrap pr-8"}>
<div
className={"whitespace-nowrap lg:w-1/4 w-1/3 overflow-hidden overflow-ellipsis pr-2"}
title={getStream(field.name).name}
>
Name: <b>{getStream(field.name).name}</b>
</div>
<div
className={"whitespace-nowrap lg:w-1/4 w-1/3 overflow-hidden overflow-ellipsis pr-2"}
title={getStream(field.name).type}
>
Type: <b>{getStream(field.name).type}</b>
</div>
<div className={"whitespace-nowrap lg:w-1/4 w-1/3 overflow-hidden overflow-ellipsis pr-2"}>
Table Name:
<b title={`${initialSourceData.sourceId}_${getStream(field.name).name ?? "[Name]"}`}>
{`${initialSourceData.sourceId}_${getStream(field.name).name ?? "[Name]"}`}
</b>
</div>
<div className={"w-full lg:w-1/4 lg:text-right lg:pr-8 overflow-hidden overflow-ellipsis"}>
{getFormErrors(field.name).length > 0 && (
<span style={{ color: "red" }}> {getFormErrors(field.name).length} errors</span>
)}
</div>
</div>
}
extra={
<DeleteOutlined
className={styles.delete}
onClick={event => {
remove(field.name, operation)
event.stopPropagation()
}}
/>
}
>
<div className={styles.item} key={field.name}>
<>
<Row>
<Col span={16}>
<Form.Item
className="form-field_fixed-label"
label={<>Name:</>}
name={[field.name, "name"]}
rules={[
{
required: true,
message: "Field is required. You can remove this collection.",
},
{
validator: (rule: any, value: string) => {
const formValues = form.getFieldsValue()
const isError = formValues.collections
.map((collection, index) => index !== field.name && collection.name)
.includes(value)
return isError
? Promise.reject("Name must be unique under the current collection")
: Promise.resolve()
},
},
]}
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
>
<Input
autoComplete="off"
ref={input}
onChange={e => handleTouchParameter(field.name, e)}
/>
</Form.Item>
</Col>
</Row>
{getCollectionParameters(field.name).map((collection: CollectionParameter) => (
<SourceEditorFormStreamsCollectionsField
documentation={collection.documentation}
field={field}
key={collection.id}
collection={collection}
// handleFormFieldsChange={}
/>
))}
</>
</div>
</Panel>
)
})}
</Collapse>
</>
)}
</Form.List>
</Form>
)
}
Example #19
Source File: OnboardingClientDocs.tsx From jitsu with MIT License | 4 votes |
OnboardingClientDocs: React.FC<Props> = ({ token }) => {
const services = useServices()
const [segmentEnabled, setSegmentEnabled] = useState<boolean>(false)
const domain = useMemo<string>(() => {
const customDomain = services.features.enableCustomDomains ? "https://t.jitsu.com" : null
return (
customDomain ||
getDomainsSelectionByEnv(services.features.environment)[0] ||
services.features.jitsuBaseUrl ||
"REPLACE_WITH_JITSU_DOMAIN"
)
}, [services.features.enableCustomDomains, services.features.jitsuBaseUrl])
const exampleSwitches = (
<div className="api-keys-doc-embed-switches">
<Space>
<LabelWithTooltip
documentation={
<>
Check if you want to intercept events from Segment (
<a href="https://jitsu.com/docs/sending-data/js-sdk/snippet#intercepting-segment-events">Read more</a>)
</>
}
render="Intercept Segment events"
/>
<Switch
size="small"
checked={segmentEnabled}
onChange={() => setSegmentEnabled(segmentEnabled => !segmentEnabled)}
/>
</Space>
</div>
)
return (
<div className={styles.container}>
<Collapse
style={{
fontSize: "17px",
}}
bordered={false}
defaultActiveKey={["1"]}
expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}
className={styles.onboardingDocsCustomCollapse}
>
<Collapse.Panel header="Embed in HTML" key="1" className="site-collapse-custom-panel">
<p className="api-keys-documentation-tab-description">
Easiest way to start tracking events within your web app is to add following snippet to{" "}
<CodeInline><head></CodeInline> section of your html file:
</p>
<CodeSnippet size="small" language="html" extra={exampleSwitches}>
{getEmbeddedHtml(segmentEnabled, token.jsAuth, domain)}
</CodeSnippet>
</Collapse.Panel>
<Collapse.Panel header="Use NPM or YARN" key="2" className="site-collapse-custom-panel">
<p className="api-keys-documentation-tab-description">
<span className={styles.textBlock}>Use</span>
<CodeSnippet size="small" language="bash">
npm install --save @jitsu/sdk-js
</CodeSnippet>
<span className={styles.textBlock}>or</span>
<CodeSnippet size="small" language="bash">
yarn add @jitsu/sdk-js
</CodeSnippet>
<br />
<span className={styles.textBlock}>Then just follow this example:</span>
<CodeSnippet size="small" language="javascript">
{getNPMDocumentation(token.jsAuth, domain)}
</CodeSnippet>
</p>
Read more about configuration properties{" "}
<a href="https://jitsu.com/docs/sending-data/js-sdk/package">in documentation</a>.
</Collapse.Panel>
<Collapse.Panel header="Server to Server" key="3" className="site-collapse-custom-panel">
Events can be send directly to API end-point. In that case, server secret should be used. Please, see curl
example:
<CodeSnippet size="small" language="bash">
{getCurlDocumentation(token.serverAuth, domain)}
</CodeSnippet>
</Collapse.Panel>
</Collapse>
</div>
)
}
Example #20
Source File: TableDataItem.tsx From yugong with MIT License | 4 votes |
TableDataItem: React.FC<Props> = ({ label, onMinus, value, onChange }) => { const [currentdataType, setCurrentDataType] = useState<string>(value?.dataType || ''); const [showOptions, setShowOptions] = useState(false); const { disabled, dataSource } = useContext(TableModuleContext); const onChangeValue = useCallback((data: { [keys in keyof TableDataItemValue]?: string }) => { onChange?.({ ...value, ...data }) }, [onChange, value]); const onChangeType = useCallback( (e) => { setCurrentDataType(e) onChangeValue({ dataType: e, format: undefined }) }, [onChangeValue], ) const onChangeFormat = useCallback( (e) => { onChangeValue({ format: e }) }, [onChangeValue], ) const renderNumber = useCallback( () => <Select placeholder="请选择数据格式" onChange={onChangeFormat} className={classNames(s.format)} value={value?.format} > {Object.keys(dataTypeFormat.number).map( key => <Select.Option key={key} value={key}> {dataTypeFormat.number[key]} </Select.Option> )} </Select>, [onChangeFormat, value?.format], ) const renderDate = useCallback( () => <Select placeholder="请选择数据格式" className={classNames(s.format)} onChange={onChangeFormat} value={value?.format} > {Object.keys(dataTypeFormat.date).map( key => <Select.Option key={key} value={key}> {dataTypeFormat.date[key]} </Select.Option> )} </Select>, [onChangeFormat, value?.format], ) const onChangeImgFormat = useCallback( (val, type: ('width' | 'height')) => { const data = value?.format?.split(',') || []; if (type === 'width') data[0] = val || ''; if (type === 'height') data[1] = val || ''; onChangeFormat(data.join(',')); }, [onChangeFormat, value?.format], ) const renderImage = useCallback( () => <> <Input placeholder='宽度' className={classNames(s.format)} style={{ width: '25%' }} onChange={e => onChangeImgFormat(e.target.value, 'width')} value={value?.format?.split(',')[0]} /> <Input placeholder='高度' className={classNames(s.format)} style={{ width: '25%' }} onChange={e => onChangeImgFormat(e.target.value, 'height')} value={value?.format?.split(',')[1]} /> </>, [onChangeImgFormat, value?.format], ) return ( <div className={s.root}> <LineItem label={<div className={s.dragwrap}><span className={s.drag}><DragHandle /></span>{label || '列'}</div>}> <Input disabled={disabled} className={s.inp} onChange={(e) => onChangeValue({ headName: e.target.value })} value={value?.headName} placeholder="名称" suffix={<HtmlSuffix />} /> <Input name="rowMap" disabled={disabled} className={classNames(s.inp, s.nbl, s.nbrad)} onChange={(e) => onChangeValue({ rowMap: e.target.value })} value={value?.rowMap} placeholder="字段" suffix={ <HtmlSuffix info={ <> <div>可用字段:</div> <Space wrap> { Object.keys(dataSource?.[0] || {})?.map(key => <Tag className={s.tag} key={key} onClick={() => onChangeValue({ rowMap: `{{${key}}}` })}> {`{{${key}}}`} </Tag> ) } </Space> </> } /> } /> <Button disabled={disabled} className={classNames(s.btn, s.nbl, s.nbr)} icon={showOptions ? <CaretDownOutlined /> : <CaretRightOutlined />} onClick={() => setShowOptions(!showOptions)} /> <Button disabled={disabled} className={s.btn} icon={<MinusOutlined />} onClick={() => onMinus?.()} /> </LineItem> <div style={{ display: showOptions ? 'block' : 'none' }}> <LineItem label=""> <LineItem label="数据类别"> <div className={s.datatype}> <Select className={classNames(s.nbrad)} style={{ flex: 'auto' }} disabled={disabled} value={value?.dataType} placeholder="默认为字符" onChange={onChangeType}> { Object.keys(dataType).map(key => <Select.Option key={key} value={key}>{dataType[key]}</Select.Option>) } </Select> {currentdataType === 'number' ? renderNumber() : null} {currentdataType === 'date' ? renderDate() : null} {currentdataType === 'image' ? renderImage() : null} </div> </LineItem> <LineItem label="列宽"> <Input disabled={disabled} placeholder="ex:50%或50px, 默认自动" value={value?.columWidth} onChange={(e) => onChangeValue({ columWidth: e.target.value })} /> </LineItem> </LineItem> </div> </div> ) }
Example #21
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 #22
Source File: GeneralCheckbox.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
export function GeneralCheckboxItem(
props: GeneralCheckboxProps
): React.ReactElement {
const {
formElement,
onChange,
colSpan,
optionGroups,
isGroup,
text,
disabled,
value,
options,
type,
...inputProps
} = props;
const isGridType = !isNil(colSpan);
const groupMap = useMemo(() => {
if (optionGroups && isGroup) {
return new Map<string, any>(
optionGroups.map((group) => {
return [
group.key,
{
...group,
optionKeys: group.options.map((v) => v.value),
},
];
})
);
} else {
return null;
}
}, [optionGroups, isGroup]);
const groupStatusMap = useMemo(() => {
if (groupMap) {
const statusMap = new Map<string, any>(
Array.from(groupMap, (v) => {
const { optionKeys } = v[1];
const checkedItems = new Set(
value?.filter((v) => optionKeys.includes(v)) ?? []
);
const status = {
indeterminate:
checkedItems.size !== 0 &&
checkedItems.size !== optionKeys.length,
checked: checkedItems.size === optionKeys.length,
};
return [v[0], status];
})
);
return statusMap;
} else {
return null;
}
}, [value, groupMap]);
const getCheckboxItem = (
item: CheckboxOptionType,
isGridType: boolean
): React.ReactElement => {
const checkboxColor = (item as CheckboxOtherOptionType)?.checkboxColor;
const checkboxColorStyle = checkboxColor
? `checkbox-${checkboxColor}`
: undefined;
const icon = (item as CheckboxOtherOptionType)?.icon;
let iconNode: JSX.Element = null;
if (icon) {
if ("imgSrc" in icon) {
const mergedIcon: SrcIcon = {
imgSrc: icon.imgSrc,
imgStyle: {
marginRight: "8px",
verticalAlign: "-0.42em",
...icon.imgStyle,
},
};
iconNode = <GeneralIcon icon={mergedIcon} size={22} />;
} else {
iconNode = (
<GeneralIcon
icon={icon}
style={{
fontSize: "22px",
marginRight: "8px",
verticalAlign: "-0.25em",
}}
size={22}
/>
);
}
}
const checkbox = (
<Checkbox
value={item.value}
key={item.value}
disabled={!!item.disabled}
className={styles[`${checkboxColorStyle}`]}
>
{iconNode}
{item.label}
</Checkbox>
);
return isGridType ? (
<Col key={item.value} span={colSpan}>
{checkbox}
</Col>
) : (
checkbox
);
};
const handleHeaderClick = (e: React.MouseEvent): void => {
e.stopPropagation();
};
const groupOnChange = (
groupValue: CheckboxValueType[],
groupKey: string
): void => {
const toCheckAll = !!groupValue.length;
const { optionKeys } = groupMap.get(groupKey);
const newValue = toCheckAll
? uniq((value ?? []).concat(optionKeys))
: value.filter((v) => !optionKeys.includes(v));
onChange(newValue);
};
const getCheckboxGroup = (
options: CheckboxOptionType[],
isGridType: boolean
): React.ReactNode => {
const groups = options.map((item) => getCheckboxItem(item, isGridType));
return isGridType ? (
<Row gutter={[16, 12]} className={styles.checkboxColLayout}>
{groups}
</Row>
) : (
groups
);
};
const renderOptions = (): React.ReactNode => {
if (isGroup && optionGroups) {
return (
<Collapse
className={styles.groupCollapse}
defaultActiveKey={optionGroups.map((g) => g.key)}
ghost
expandIcon={({ isActive }) => (
<CaretRightOutlined rotate={isActive ? 90 : 0} />
)}
>
{optionGroups.map((group) => (
<Collapse.Panel
header={
<span onClick={handleHeaderClick}>
<Checkbox.Group
onChange={(v) => groupOnChange(v, group.key)}
value={
groupStatusMap?.get(group.key).checked ? [group.key] : []
}
>
<Checkbox
key={group.key}
value={group.key}
{...groupStatusMap?.get(group.key)}
>
{group.name}
</Checkbox>
</Checkbox.Group>
</span>
}
key={group.key}
>
{getCheckboxGroup(group.options, isGridType)}
</Collapse.Panel>
))}
</Collapse>
);
} else {
return getCheckboxGroup(options, isGridType);
}
};
if (type === "icon") {
return (
<IconCheckbox
options={options}
name={props.name}
value={value}
isCustom={props.isCustom}
disabled={disabled}
onChange={onChange}
/>
);
}
return (isGroup && optionGroups) || options?.length > 0 ? (
<Checkbox.Group
className={styles.generalCheckBox}
value={value as CheckboxValueType[]}
onChange={onChange}
style={{ width: "100%" }}
data-testid="checkbox-form-item"
{...inputProps}
>
{renderOptions()}
</Checkbox.Group>
) : (
<Checkbox
checked={value as boolean}
onChange={(e) => {
onChange?.(e.target.checked);
}}
disabled={disabled}
>
{text}
</Checkbox>
);
}
Example #23
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 #24
Source File: Projectdetails.tsx From RareCamp with Apache License 2.0 | 4 votes |
export default function ProjectDetails({ project }) {
const [isProjectVisible, setIsProjectVisible] = useState(true)
const queryClient = useQueryClient()
const [taskForm] = Form.useForm()
const createTaskMutation = useMutation(
(task: any) =>
axios.post(`/projects/${task.projectId}/tasks`, {
task,
}),
{
async onSuccess(resp) {
notification.success({
duration: 2,
message: 'Task has been created successfully',
})
const { data } = await queryClient.getQueryData<any>([
'program',
project.programId,
])
addTaskToProject(
data.program,
project.projectId,
resp.data.task,
)
queryClient.setQueryData(['program', project.programId], {
data,
})
taskForm.resetFields()
},
onError(err: Error) {
notification.error({
duration: 2,
message: 'Error while creating Task',
description: String(err),
})
},
},
)
function submitTask(evt) {
if (evt.keyCode === 13) {
taskForm.validateFields().then((values) => {
createTaskMutation.mutate({
...values,
projectId: project.projectId,
})
})
}
}
const toggleTasksVisibility = () =>
setIsProjectVisible(!isProjectVisible)
return (
<>
<tr key={project.projectId} className="ant-table-row">
<td colSpan={6} className="ant-table-cell project-name">
<div className="project-header">
<Space size={9}>
<EditProject project={project} />
<div
onClick={toggleTasksVisibility}
onKeyPress={toggleTasksVisibility}
role="button"
tabIndex={project.projectId}
>
<Space>
{isProjectVisible ? (
<CaretDownOutlined />
) : (
<CaretRightOutlined />
)}
<span>{project.name}</span>
</Space>
</div>
{project.description ? (
<Tooltip
placement="bottom"
title={project.description}
>
<InfoCircleOutlined />
</Tooltip>
) : null}
</Space>
</div>
</td>
</tr>
{isProjectVisible ? (
<>
{project.tasks.map((task) => (
<TaskRow
key={JSON.stringify(task)}
task={task}
programId={project.programId}
/>
))}
<tr className="ant-table-row add-task-row">
<td colSpan={6} className="ant-table-cell">
<Form form={taskForm} name={`test${project.projectId}`}>
<Form.Item
name="name"
required={false}
rules={[{ required: true, message: '' }]}
>
<Input
prefix={
createTaskMutation.isLoading ? (
<LoadingOutlined style={{ fontSize: 20 }} />
) : null
}
placeholder="+ Add Task"
onKeyUp={submitTask}
/>
</Form.Item>
</Form>
</td>
</tr>
</>
) : null}
</>
)
}
Example #25
Source File: RepositoryTab.tsx From posthog-foss with MIT License | 4 votes |
export function RepositoryTab(): JSX.Element {
const { repositoryLoading, filteredUninstalledPlugins } = useValues(pluginsLogic)
const [repositorySectionsOpen, setRepositorySectionsOpen] = useState([
RepositorySection.Official,
RepositorySection.Community,
])
const officialPlugins = filteredUninstalledPlugins.filter((plugin) => plugin.maintainer === 'official')
const communityPlugins = filteredUninstalledPlugins.filter((plugin) => plugin.maintainer === 'community')
const toggleRepositorySectionOpen = (section: RepositorySection): void => {
if (repositorySectionsOpen.includes(section)) {
setRepositorySectionsOpen(repositorySectionsOpen.filter((s) => section !== s))
return
}
setRepositorySectionsOpen([...repositorySectionsOpen, section])
}
return (
<div>
<Subtitle subtitle="Plugin Repository" />
<PluginsSearch />
<div>
{(!repositoryLoading || filteredUninstalledPlugins.length > 0) && (
<>
<Row gutter={16} style={{ marginTop: 16, display: 'block' }}>
<div
className="plugins-repository-tab-section-header"
onClick={() => toggleRepositorySectionOpen(RepositorySection.Official)}
>
<Subtitle
subtitle={
<>
{repositorySectionsOpen.includes(RepositorySection.Official) ? (
<CaretDownOutlined />
) : (
<CaretRightOutlined />
)}
{` Official Plugins (${officialPlugins.length})`}
</>
}
/>
</div>
{repositorySectionsOpen.includes(RepositorySection.Official) && (
<>
<Col span={24}>
{officialPlugins.length > 0
? 'Official plugins are built and maintained by the PostHog team.'
: 'You have already installed all official plugins!'}
</Col>
<br />
{officialPlugins.map((plugin) => {
return (
<PluginCard
key={plugin.url}
plugin={{
name: plugin.name,
url: plugin.url,
description: plugin.description,
}}
maintainer={plugin.maintainer}
/>
)
})}
</>
)}
</Row>
<Row gutter={16} style={{ marginTop: 16, display: 'block' }}>
<div
className="plugins-repository-tab-section-header"
onClick={() => toggleRepositorySectionOpen(RepositorySection.Community)}
>
<Subtitle
subtitle={
<>
{repositorySectionsOpen.includes(RepositorySection.Community) ? (
<CaretDownOutlined />
) : (
<CaretRightOutlined />
)}
{` Community Plugins (${communityPlugins.length})`}
</>
}
/>
</div>
{repositorySectionsOpen.includes(RepositorySection.Community) && (
<>
<Col span={24}>
{communityPlugins.length > 0 ? (
<span>
Community plugins are not built nor maintained by the PostHog team.{' '}
<a
href="https://posthog.com/docs/plugins/build"
target="_blank"
rel="noopener"
>
Anyone, including you, can build a plugin.
</a>
</span>
) : (
'You have already installed all community plugins!'
)}
</Col>
<br />
{communityPlugins.map((plugin) => {
return (
<PluginCard
key={plugin.url}
plugin={{
name: plugin.name,
url: plugin.url,
description: plugin.description,
}}
maintainer={plugin.maintainer}
/>
)
})}
</>
)}
</Row>
</>
)}
</div>
{repositoryLoading && filteredUninstalledPlugins.length === 0 && (
<Row gutter={16}>
<PluginLoading />
</Row>
)}
</div>
)
}
Example #26
Source File: EnabledPluginsSection.tsx From posthog-foss with MIT License | 4 votes |
export function EnabledPluginSection(): JSX.Element {
const { user } = useValues(userLogic)
const {
rearrange,
setTemporaryOrder,
cancelRearranging,
savePluginOrders,
makePluginOrderSaveable,
toggleSectionOpen,
} = useActions(pluginsLogic)
const {
enabledPlugins,
filteredEnabledPlugins,
sortableEnabledPlugins,
unsortableEnabledPlugins,
rearranging,
loading,
temporaryOrder,
pluginOrderSaveable,
searchTerm,
sectionsOpen,
} = useValues(pluginsLogic)
const canRearrange: boolean = canConfigurePlugins(user?.organization) && sortableEnabledPlugins.length > 1
const rearrangingButtons = rearranging ? (
<>
<Button
type="primary"
icon={<SaveOutlined />}
loading={loading}
onClick={(e) => {
e.stopPropagation()
savePluginOrders(temporaryOrder)
}}
disabled={!pluginOrderSaveable}
>
Save order
</Button>
<Button
type="default"
icon={<CloseOutlined />}
onClick={(e) => {
cancelRearranging()
e.stopPropagation()
}}
>
Cancel
</Button>
</>
) : (
<Tooltip
title={
enabledPlugins.length <= 1 ? (
'At least two plugins need to be enabled for reordering.'
) : (
<>
{!!searchTerm ? (
'Editing the order of plugins is disabled when searching.'
) : (
<>
Order matters because event processing with plugins works like a pipe: the event is
processed by every enabled plugin <b>in sequence</b>.
</>
)}
</>
)
}
placement="top"
>
<Button
icon={<OrderedListOutlined />}
onClick={(e) => {
e.stopPropagation()
rearrange()
}}
disabled={!!searchTerm || sortableEnabledPlugins.length <= 1}
>
Edit order
</Button>
</Tooltip>
)
const onSortEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }): void => {
if (oldIndex === newIndex) {
return
}
const move = (arr: PluginTypeWithConfig[], from: number, to: number): { id: number; order: number }[] => {
const clone = [...arr]
Array.prototype.splice.call(clone, to, 0, Array.prototype.splice.call(clone, from, 1)[0])
return clone.map(({ id }, order) => ({ id, order: order + 1 }))
}
const movedPluginId: number = enabledPlugins[oldIndex]?.id
const newTemporaryOrder: Record<number, number> = {}
for (const { id, order } of move(enabledPlugins, oldIndex, newIndex)) {
newTemporaryOrder[id] = order
}
if (!rearranging) {
rearrange()
}
setTemporaryOrder(newTemporaryOrder, movedPluginId)
}
const EnabledPluginsHeader = (): JSX.Element => (
<div className="plugins-installed-tab-section-header" onClick={() => toggleSectionOpen(PluginSection.Enabled)}>
<Subtitle
subtitle={
<>
{sectionsOpen.includes(PluginSection.Enabled) ? <CaretDownOutlined /> : <CaretRightOutlined />}
{` Enabled plugins (${filteredEnabledPlugins.length})`}
{rearranging && sectionsOpen.includes(PluginSection.Enabled) && (
<Tag color="red" style={{ fontWeight: 'normal', marginLeft: 10 }}>
Reordering in progress
</Tag>
)}
</>
}
buttons={<Space>{sectionsOpen.includes(PluginSection.Enabled) && rearrangingButtons}</Space>}
/>
</div>
)
if (enabledPlugins.length === 0) {
return (
<>
<EnabledPluginsHeader />
{sectionsOpen.includes(PluginSection.Enabled) && <p style={{ margin: 10 }}>No plugins enabled.</p>}
</>
)
}
return (
<>
<EnabledPluginsHeader />
{sectionsOpen.includes(PluginSection.Enabled) && (
<>
{sortableEnabledPlugins.length === 0 && unsortableEnabledPlugins.length === 0 && (
<p style={{ margin: 10 }}>No plugins match your search.</p>
)}
{canRearrange || rearranging ? (
<>
{sortableEnabledPlugins.length > 0 && (
<>
<SortablePlugins
useDragHandle
onSortEnd={onSortEnd}
onSortOver={makePluginOrderSaveable}
>
{sortableEnabledPlugins.map((plugin, index) => (
<SortablePlugin
key={plugin.id}
plugin={plugin}
index={index}
order={index + 1}
maxOrder={enabledPlugins.length}
rearranging={rearranging}
/>
))}
</SortablePlugins>
</>
)}
</>
) : (
<Row gutter={16} style={{ marginTop: 16 }}>
{sortableEnabledPlugins.length > 0 && (
<>
{sortableEnabledPlugins.map((plugin, index) => (
<InstalledPlugin
key={plugin.id}
plugin={plugin}
order={index + 1}
maxOrder={filteredEnabledPlugins.length}
/>
))}
</>
)}
</Row>
)}
{unsortableEnabledPlugins.map((plugin) => (
<InstalledPlugin
key={plugin.id}
plugin={plugin}
maxOrder={enabledPlugins.length}
rearranging={rearranging}
unorderedPlugin
/>
))}
</>
)}
</>
)
}