@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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
iconMap.set('run', { icon: <CaretRightOutlined />, text: '试一试' })
Example #7
Source File: collapse.tsx    From XFlow with MIT License 5 votes vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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:&nbsp;&nbsp;<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:&nbsp;&nbsp;<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:&nbsp;&nbsp;
                          <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 vote down vote up
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>&lt;head&gt;</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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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
                        />
                    ))}
                </>
            )}
        </>
    )
}