mirror of
https://gitee.com/dcren/cloud-native-app-initializer.git
synced 2026-08-25 19:06:31 +08:00
Project initial
This commit is contained in:
43
initializer-page/src/components/common/builder/Control.js
Normal file
43
initializer-page/src/components/common/builder/Control.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
const Control = ({ text, children, variant }) => {
|
||||
if (variant === 'xl' || variant === 'xxl') {
|
||||
return (
|
||||
<div className='colset'>
|
||||
<div className='left'>
|
||||
<div className='sticky-label'>{text}</div>
|
||||
</div>
|
||||
<div
|
||||
className={`dependencies-box ${variant === 'xl' ? 'list' : 'large'}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='colset'>
|
||||
<div className='left'>
|
||||
<>{text}</>
|
||||
</div>
|
||||
<div className={`right ${variant === 'md' ? 'right-md' : ''}`}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Control.defaultProps = {
|
||||
children: null,
|
||||
variant: '',
|
||||
}
|
||||
|
||||
Control.propTypes = {
|
||||
children: PropTypes.node,
|
||||
variant: PropTypes.string,
|
||||
text: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
export default Control
|
||||
18
initializer-page/src/components/common/builder/FieldError.js
Normal file
18
initializer-page/src/components/common/builder/FieldError.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
function FieldError({ children }) {
|
||||
return (
|
||||
<div className='control-error'>
|
||||
<p className='title'>
|
||||
<strong>{children}</strong>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
FieldError.propTypes = {
|
||||
children: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
export default FieldError
|
||||
36
initializer-page/src/components/common/builder/FieldInput.js
Normal file
36
initializer-page/src/components/common/builder/FieldInput.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
const FieldInput = ({ id, text, value, onChange, disabled, inputRef }) => (
|
||||
<div className='control'>
|
||||
<label htmlFor={id}>{text}</label>
|
||||
<input
|
||||
type='text'
|
||||
id={id}
|
||||
className='control-input'
|
||||
disabled={disabled}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={inputRef}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
FieldInput.defaultProps = {
|
||||
disabled: false,
|
||||
inputRef: null,
|
||||
}
|
||||
|
||||
FieldInput.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
inputRef: PropTypes.oneOfType([
|
||||
PropTypes.func,
|
||||
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
|
||||
]),
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default FieldInput
|
||||
38
initializer-page/src/components/common/builder/FieldRadio.js
Normal file
38
initializer-page/src/components/common/builder/FieldRadio.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
import {RadioGroup} from '../form'
|
||||
|
||||
const FieldRadio = ({ id, text, value, onChange, disabled, options }) => (
|
||||
<div className='control'>
|
||||
<label htmlFor={id}>{text}</label>
|
||||
<RadioGroup
|
||||
name='packaging'
|
||||
disabled={disabled}
|
||||
selected={value}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
FieldRadio.defaultProps = {
|
||||
disabled: false,
|
||||
options: [],
|
||||
}
|
||||
|
||||
FieldRadio.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
key: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
})
|
||||
),
|
||||
}
|
||||
|
||||
export default FieldRadio
|
||||
26
initializer-page/src/components/common/builder/HotKeys.js
Normal file
26
initializer-page/src/components/common/builder/HotKeys.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import {GlobalHotKeys} from 'react-hotkeys'
|
||||
|
||||
function HotKeys({ onSubmit, onExplore }) {
|
||||
return (
|
||||
<GlobalHotKeys
|
||||
keyMap={{
|
||||
SUBMIT: ['command+enter', 'ctrl+enter'],
|
||||
EXPLORE: ['ctrl+space'],
|
||||
}}
|
||||
handlers={{
|
||||
SUBMIT: onSubmit,
|
||||
EXPLORE: onExplore,
|
||||
}}
|
||||
global
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
HotKeys.propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
onExplore: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default HotKeys
|
||||
42
initializer-page/src/components/common/builder/Loading.js
Normal file
42
initializer-page/src/components/common/builder/Loading.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react'
|
||||
|
||||
import Control from './Control'
|
||||
import {Footer} from '../layout'
|
||||
import {Placeholder} from '../form'
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<>
|
||||
<Control text='项目构建方式'>
|
||||
<Placeholder type='radios' count={2} width='133px' />
|
||||
</Control>
|
||||
<Control text='开发语言'>
|
||||
<Placeholder type='radios' count={3} width='73px' />
|
||||
</Control>
|
||||
<Control text='Spring Boot 版本'>
|
||||
<Placeholder type='radios' count={5} width='105px' />
|
||||
</Control>
|
||||
<Control text='项目元信息' special='md'>
|
||||
<div>
|
||||
<div className='control'>
|
||||
<Placeholder type='input' />
|
||||
</div>
|
||||
<div className='control'>
|
||||
<Placeholder type='input' />
|
||||
</div>
|
||||
<div className='control'>
|
||||
<Placeholder type='dropdown' />
|
||||
</div>
|
||||
</div>
|
||||
</Control>
|
||||
<Control text='组件依赖'>
|
||||
<Placeholder type='tabs' count={2} />
|
||||
</Control>
|
||||
<Footer>
|
||||
<Placeholder type='button' width='189px' />
|
||||
<Placeholder type='button' width='212px' />
|
||||
<Placeholder type='button' width='110px' />
|
||||
</Footer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
59
initializer-page/src/components/common/builder/PanelMore.js
Normal file
59
initializer-page/src/components/common/builder/PanelMore.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import get from 'lodash.get'
|
||||
import React, {useContext, useEffect} from 'react'
|
||||
|
||||
import {AppContext} from '../../reducer/App'
|
||||
import {IconChevronRight} from '../icons'
|
||||
|
||||
const PanelMore = ({ children, fieldFocusOnOpen }) => {
|
||||
const { more, dispatch } = useContext(AppContext)
|
||||
|
||||
useEffect(() => {
|
||||
if (more && fieldFocusOnOpen) {
|
||||
setTimeout(() => {
|
||||
get(fieldFocusOnOpen, 'current').focus()
|
||||
}, 300)
|
||||
}
|
||||
}, [more, fieldFocusOnOpen])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='more'>
|
||||
<div className='wrap'>
|
||||
<a
|
||||
href='/'
|
||||
onClick={event => {
|
||||
event.preventDefault()
|
||||
dispatch({
|
||||
type: 'UPDATE',
|
||||
payload: { more: !more },
|
||||
})
|
||||
}}
|
||||
className={more ? 'toggle' : ''}
|
||||
>
|
||||
<IconChevronRight />
|
||||
{!more ? '高级选项' : '高级选项'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`panel ${more ? 'panel-active' : ''}`}>
|
||||
<div className='panel-wrap'>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PanelMore.defaultProps = {
|
||||
children: null,
|
||||
fieldFocusOnOpen: null,
|
||||
}
|
||||
|
||||
PanelMore.propTypes = {
|
||||
children: PropTypes.node,
|
||||
fieldFocusOnOpen: PropTypes.oneOfType([
|
||||
PropTypes.func,
|
||||
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
|
||||
]),
|
||||
}
|
||||
|
||||
export default PanelMore
|
||||
73
initializer-page/src/components/common/builder/Tabs.js
Normal file
73
initializer-page/src/components/common/builder/Tabs.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import get from 'lodash.get'
|
||||
import React, {useContext, useEffect, useState} from 'react'
|
||||
|
||||
import {AppContext} from '../../reducer/App'
|
||||
import {IconList, IconSearch} from '../icons'
|
||||
import {InitializrContext} from '../../reducer/Initializr'
|
||||
|
||||
const Tabs = ({ changeTab }) => {
|
||||
const { dependencies: deps, dispatch, tab } = useContext(AppContext)
|
||||
const { values } = useContext(InitializrContext)
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
setCount(
|
||||
values.dependencies.filter(d => {
|
||||
return get(deps, 'list', []).find(a => a.id === d).valid
|
||||
}).length
|
||||
)
|
||||
}, [deps, values.dependencies])
|
||||
|
||||
const change = newTab => {
|
||||
dispatch({
|
||||
type: 'UPDATE',
|
||||
payload: { tab: newTab },
|
||||
})
|
||||
setTimeout(() => {
|
||||
changeTab(newTab)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='tab'>
|
||||
<div className='tab-container'>
|
||||
<a
|
||||
href='/'
|
||||
aria-label='Search'
|
||||
onClick={event => {
|
||||
event.preventDefault()
|
||||
change('quicksearch')
|
||||
}}
|
||||
className={`quick-search ${tab === 'quicksearch' ? 'active' : ''}`}
|
||||
>
|
||||
<IconSearch /><span>搜索</span>
|
||||
</a>
|
||||
<a
|
||||
href='/'
|
||||
aria-label='List'
|
||||
onClick={event => {
|
||||
event.preventDefault()
|
||||
change('list')
|
||||
}}
|
||||
className={`list ${tab === 'list' ? 'active' : ''}`}
|
||||
>
|
||||
<IconList /><span>分组列表</span>
|
||||
</a>
|
||||
{count > 0 && (
|
||||
<>
|
||||
<strong>
|
||||
<span>{count}</span> 项已选择
|
||||
</strong>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Tabs.propTypes = {
|
||||
changeTab: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default Tabs
|
||||
83
initializer-page/src/components/common/builder/Warnings.js
Normal file
83
initializer-page/src/components/common/builder/Warnings.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import get from 'lodash.get'
|
||||
import React, {useContext} from 'react'
|
||||
|
||||
import {IconTimes} from '../icons'
|
||||
import {InitializrContext} from '../../reducer/Initializr'
|
||||
|
||||
function Warnings() {
|
||||
const { warnings, dispatch } = useContext(InitializrContext)
|
||||
if (Object.keys(warnings).length > 0) {
|
||||
return (
|
||||
<div className='colset'>
|
||||
<div className='left' />
|
||||
<div className='right'>
|
||||
<div className='warnings'>
|
||||
<a
|
||||
className='close'
|
||||
href='/#'
|
||||
onClick={event => {
|
||||
event.preventDefault()
|
||||
dispatch({
|
||||
type: 'CLEAR_WARNINGS',
|
||||
})
|
||||
}}
|
||||
>
|
||||
<IconTimes />
|
||||
</a>
|
||||
The following attributes could not be handled:
|
||||
<ul>
|
||||
{get(warnings, 'project') && (
|
||||
<li>
|
||||
<strong>{get(warnings, 'project.value')}</strong> is not a
|
||||
valid project type,{' '}
|
||||
<strong>{get(warnings, 'project.select')}</strong> has been
|
||||
selected.
|
||||
</li>
|
||||
)}
|
||||
{get(warnings, 'language') && (
|
||||
<li>
|
||||
<strong>{get(warnings, 'language.value')}</strong> is not a
|
||||
valid language,{' '}
|
||||
<strong>{get(warnings, 'language.select')}</strong> has been
|
||||
selected.
|
||||
</li>
|
||||
)}
|
||||
{get(warnings, 'boot') && (
|
||||
<li>
|
||||
Spring Boot <strong>{get(warnings, 'boot.value')}</strong> is
|
||||
not available, <strong>{get(warnings, 'boot.select')}</strong>{' '}
|
||||
has been selected.
|
||||
</li>
|
||||
)}
|
||||
{get(warnings, 'meta.java') && (
|
||||
<li>
|
||||
<strong>{get(warnings, 'meta.java.value')}</strong> is not a
|
||||
valid Java version,{' '}
|
||||
<strong>{get(warnings, 'meta.java.select')}</strong> has been
|
||||
selected.
|
||||
</li>
|
||||
)}
|
||||
{get(warnings, 'meta.packaging') && (
|
||||
<li>
|
||||
<strong>{get(warnings, 'meta.packaging.value')}</strong> is
|
||||
not a valid packaging,{' '}
|
||||
<strong>{get(warnings, 'meta.packaging.select')}</strong> has
|
||||
been selected.
|
||||
</li>
|
||||
)}
|
||||
{get(warnings, 'dependencies') && (
|
||||
<li>
|
||||
The following dependencies are not supported:{' '}
|
||||
<strong>{get(warnings, 'dependencies.value')}</strong>.
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
|
||||
export default Warnings
|
||||
10
initializer-page/src/components/common/builder/index.js
Normal file
10
initializer-page/src/components/common/builder/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export { default as Control } from './Control'
|
||||
export { default as FieldInput } from './FieldInput'
|
||||
export { default as FieldRadio } from './FieldRadio'
|
||||
export { default as FieldError } from './FieldError'
|
||||
export { default as PanelMore } from './PanelMore'
|
||||
export { default as Tabs } from './Tabs'
|
||||
export { default as QuickSearch } from './quick-search/QuickSearch'
|
||||
export { default as List } from './list/List'
|
||||
export { default as Loading } from './Loading'
|
||||
export { default as Warnings } from './Warnings'
|
||||
47
initializer-page/src/components/common/builder/list/List.js
Normal file
47
initializer-page/src/components/common/builder/list/List.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import get from 'lodash.get'
|
||||
import React, {useContext} from 'react'
|
||||
|
||||
import ListGroup from './ListGroup'
|
||||
import {AppContext} from '../../../reducer/App'
|
||||
import {InitializrContext} from '../../../reducer/Initializr'
|
||||
|
||||
const List = () => {
|
||||
const { dependencies, dispatch, groupsOpened } = useContext(AppContext)
|
||||
const { values, dispatch: dispatchInitializr } = useContext(InitializrContext)
|
||||
return (
|
||||
<div className='groups'>
|
||||
{get(dependencies, 'groups').map(group => {
|
||||
return (
|
||||
<ListGroup
|
||||
group={group.group}
|
||||
key={group.group}
|
||||
dependencyGroup={group}
|
||||
add={id => {
|
||||
dispatchInitializr({
|
||||
type: 'ADD_DEPENDENCY',
|
||||
payload: { id },
|
||||
})
|
||||
}}
|
||||
remove={id => {
|
||||
dispatchInitializr({
|
||||
type: 'REMOVE_DEPENDENCY',
|
||||
payload: { id },
|
||||
})
|
||||
}}
|
||||
toggle={id => {
|
||||
dispatch({
|
||||
type: 'TOGGLE_GROUP',
|
||||
payload: { id },
|
||||
})
|
||||
}}
|
||||
itemsSelected={get(values, 'dependencies', [])}
|
||||
isClose={groupsOpened.indexOf(group.group) < 0}
|
||||
items={group.items}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default List
|
||||
@@ -0,0 +1,88 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
import ListItem from './ListItem'
|
||||
import {IconChevronRight} from '../../icons'
|
||||
|
||||
function ListGroup({
|
||||
group,
|
||||
items,
|
||||
itemsSelected,
|
||||
isClose,
|
||||
add,
|
||||
remove,
|
||||
toggle,
|
||||
}) {
|
||||
const toggleGroup = event => {
|
||||
event.preventDefault()
|
||||
toggle(group)
|
||||
}
|
||||
const onKeyDown = event => {
|
||||
const keyPressed = event.key
|
||||
if (keyPressed === 'Enter' || keyPressed === ' ') {
|
||||
toggleGroup(event)
|
||||
}
|
||||
}
|
||||
const isItemSelected = item => {
|
||||
return !!itemsSelected.find(o => o === item.id)
|
||||
}
|
||||
return (
|
||||
<div className='group'>
|
||||
<div className='group-title'>
|
||||
<a
|
||||
href='/'
|
||||
onClick={toggleGroup}
|
||||
className={!isClose ? 'toggleGroupItems' : ''}
|
||||
tabIndex={0}
|
||||
onKeyDown={onKeyDown}
|
||||
>
|
||||
<IconChevronRight />
|
||||
{group}
|
||||
</a>
|
||||
</div>
|
||||
{!isClose && (
|
||||
<div className='group-items' key={`links${group}`}>
|
||||
{items.map(item => (
|
||||
<ListItem
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
name={item.name}
|
||||
description={item.description}
|
||||
valid={item.valid}
|
||||
message={item.message}
|
||||
selected={isItemSelected(item)}
|
||||
onChange={value => {
|
||||
if (value) {
|
||||
add(item.id)
|
||||
} else {
|
||||
remove(item.id)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ListGroup.propTypes = {
|
||||
group: PropTypes.string.isRequired,
|
||||
add: PropTypes.func.isRequired,
|
||||
remove: PropTypes.func.isRequired,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
itemsSelected: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
isClose: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
description: PropTypes.string.isRequired,
|
||||
group: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
keywords: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
valid: PropTypes.bool.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
}
|
||||
|
||||
export default ListGroup
|
||||
@@ -0,0 +1,77 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
import {IconCheck, IconPlus, IconTimes} from '../../icons'
|
||||
|
||||
function ListItem({
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
valid,
|
||||
message,
|
||||
onChange,
|
||||
selected,
|
||||
}) {
|
||||
const toggle = event => {
|
||||
event.preventDefault()
|
||||
if (valid) {
|
||||
onChange(!selected)
|
||||
}
|
||||
}
|
||||
const onKeyDown = event => {
|
||||
const keyPressed = event.key
|
||||
if (keyPressed === 'Enter' || keyPressed === ' ') {
|
||||
toggle(event)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<a
|
||||
href='/'
|
||||
onClick={toggle}
|
||||
tabIndex={!valid ? -1 : ''}
|
||||
className={`${!valid ? 'invalid' : ''} ${selected ? 'checked' : ''}`}
|
||||
key={id}
|
||||
onKeyDown={onKeyDown}
|
||||
>
|
||||
<div key={`d${id}`}>
|
||||
<input
|
||||
type='checkbox'
|
||||
value={id}
|
||||
key={`ck${id}`}
|
||||
checked={selected}
|
||||
disabled={!valid}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<strong key={`ck1${id}`}>{name}</strong>
|
||||
<br key={`br${id}`} />
|
||||
{valid && <span key={`ck2${id}`}>{description}</span>}
|
||||
<span key={`ck3${id}`} className='icon'>
|
||||
<IconPlus key={`ck4${id}`} />
|
||||
<IconTimes key={`ck5${id}`} />
|
||||
<IconCheck key={`ck6${id}`} />
|
||||
</span>
|
||||
{!valid && (
|
||||
<span className='warning' key={`warning${id}`}>
|
||||
{message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
ListItem.defaultProps = {
|
||||
message: '',
|
||||
}
|
||||
|
||||
ListItem.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
valid: PropTypes.bool.isRequired,
|
||||
message: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
selected: PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
export default ListItem
|
||||
@@ -0,0 +1,75 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import {CSSTransition, TransitionGroup} from 'react-transition-group'
|
||||
|
||||
import {IconCheck, IconTimes} from '../../icons'
|
||||
|
||||
function Dependencies({ list, remove }) {
|
||||
return (
|
||||
<TransitionGroup
|
||||
component='ul'
|
||||
className='dependencies-list dependencies-list-checked'
|
||||
>
|
||||
{list.map(item => {
|
||||
return (
|
||||
<CSSTransition timeout={500} classNames='fade' key={`f${item.id}`}>
|
||||
<li>
|
||||
<span className='dependency-item-span'>
|
||||
<a
|
||||
className={`dependency-item checked ${
|
||||
!item.valid ? 'invalid' : ''
|
||||
}`}
|
||||
href='/'
|
||||
onClick={event => {
|
||||
event.preventDefault()
|
||||
remove(item)
|
||||
}}
|
||||
key={item.id}
|
||||
>
|
||||
<div key={`d1${item.id}`}>
|
||||
<strong key={`d2${item.id}`}>{item.name}</strong>
|
||||
<br key={`d3${item.id}`} />
|
||||
{item.valid && (
|
||||
<span key={`d4${item.id}`} className='description'>
|
||||
{item.description}
|
||||
</span>
|
||||
)}
|
||||
<span key={`d5${item.id}`} className='icon'>
|
||||
<IconTimes key={`d6${item.id}`} />
|
||||
<IconCheck key={`d7${item.id}`} />
|
||||
</span>
|
||||
{!item.valid && (
|
||||
<span className='warning' key={`warning${item.id}`}>
|
||||
{item.message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
</span>
|
||||
</li>
|
||||
</CSSTransition>
|
||||
)
|
||||
})}
|
||||
</TransitionGroup>
|
||||
)
|
||||
}
|
||||
|
||||
Dependencies.defaultProps = {
|
||||
list: [],
|
||||
}
|
||||
|
||||
Dependencies.propTypes = {
|
||||
list: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
group: PropTypes.string.isRequired,
|
||||
valid: PropTypes.bool.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
message: PropTypes.string,
|
||||
})
|
||||
),
|
||||
remove: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default Dependencies
|
||||
@@ -0,0 +1,189 @@
|
||||
import * as JsSearch from 'js-search'
|
||||
import PropTypes from 'prop-types'
|
||||
import get from 'lodash.get'
|
||||
import React, {useContext, useEffect, useState} from 'react'
|
||||
|
||||
import Dependencies from './Dependencies'
|
||||
import Result from './Result'
|
||||
import {AppContext} from '../../../reducer/App'
|
||||
import {InitializrContext} from '../../../reducer/Initializr'
|
||||
|
||||
const sortResult = dependencies => {
|
||||
return dependencies.sort((a, b) => {
|
||||
if (a.valid && !b.valid) {
|
||||
return -1
|
||||
}
|
||||
if (!a.valid && b.valid) {
|
||||
return 1
|
||||
}
|
||||
return b.weight - a.weight
|
||||
})
|
||||
}
|
||||
|
||||
const QuickSearch = ({ submit, input }) => {
|
||||
const { values, dispatch } = useContext(InitializrContext)
|
||||
const { dependencies: dependenciesContext } = useContext(AppContext)
|
||||
const [query, setQuery] = useState('')
|
||||
const [selected, setSelected] = useState(0)
|
||||
const [dependencies, setDependencies] = useState([])
|
||||
const [result, setResult] = useState([])
|
||||
const [count, setCount] = useState(0)
|
||||
const [search, setSearch] = useState(null)
|
||||
|
||||
const add = id => {
|
||||
dispatch({
|
||||
type: 'ADD_DEPENDENCY',
|
||||
payload: { id },
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const newDeps = get(values, 'dependencies', []).map(item => {
|
||||
return get(dependenciesContext, 'list', []).find(d => d.id === item)
|
||||
})
|
||||
setDependencies(newDeps)
|
||||
const jsSearchUp = new JsSearch.Search('name')
|
||||
jsSearchUp.addIndex('name')
|
||||
jsSearchUp.addIndex('id')
|
||||
jsSearchUp.addIndex('description')
|
||||
jsSearchUp.addIndex('group')
|
||||
jsSearchUp.addDocuments(get(dependenciesContext, 'list'))
|
||||
setSearch(jsSearchUp)
|
||||
}, [values, dependenciesContext, values.dependencies])
|
||||
|
||||
const onFocus = () => {
|
||||
setSelected(0)
|
||||
}
|
||||
|
||||
const onBlur = () => {
|
||||
setSelected(-1)
|
||||
}
|
||||
|
||||
const onKeyDown = event => {
|
||||
switch (event.keyCode) {
|
||||
case 40: // Down
|
||||
event.preventDefault()
|
||||
setSelected(Math.min(selected + 1, result.length - 1, 4))
|
||||
break
|
||||
case 38: // Up
|
||||
event.preventDefault()
|
||||
setSelected(Math.max(selected - 1, 0))
|
||||
break
|
||||
case 13: // Enter
|
||||
event.preventDefault()
|
||||
if (result.length > 0) {
|
||||
add(result[selected].id)
|
||||
setQuery('')
|
||||
} else {
|
||||
submit()
|
||||
}
|
||||
break
|
||||
case 27: // Escape
|
||||
event.preventDefault()
|
||||
setQuery('')
|
||||
break
|
||||
case 39: // Right
|
||||
case 37: // Left
|
||||
break
|
||||
default:
|
||||
// Default
|
||||
setSelected(0)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const onSearch = () => {
|
||||
if (!search) {
|
||||
return
|
||||
}
|
||||
let vals = search
|
||||
.search(query)
|
||||
.filter(
|
||||
item => !get(values, 'dependencies', []).find(o => o === item.id)
|
||||
)
|
||||
setCount(vals.length)
|
||||
if (vals.length > 5) {
|
||||
vals = vals.slice(0, 5)
|
||||
}
|
||||
vals = sortResult(vals)
|
||||
setResult(vals)
|
||||
}
|
||||
onSearch()
|
||||
}, [values, query, search, setResult])
|
||||
|
||||
return (
|
||||
<div className='colset-2'>
|
||||
<div className='column'>
|
||||
<label className='search-label' htmlFor='input-quicksearch'>
|
||||
{/* eslint-disable-line */}
|
||||
搜索依赖组件
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
className='control-input'
|
||||
placeholder='Web, Security, JPA, Actuator, Devtools...'
|
||||
value={query}
|
||||
onBlur={onBlur}
|
||||
onFocus={onFocus}
|
||||
onChange={event => {
|
||||
setQuery(event.target.value)
|
||||
setSelected(0)
|
||||
}}
|
||||
id='input-quicksearch'
|
||||
ref={input}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
<Result
|
||||
list={result}
|
||||
add={item => {
|
||||
add(item.id)
|
||||
setQuery('')
|
||||
if (input) {
|
||||
get(input, 'current').focus()
|
||||
}
|
||||
}}
|
||||
selected={selected}
|
||||
select={setSelected}
|
||||
/>
|
||||
{count > 5 && (
|
||||
<div className='search-more-warning'>
|
||||
<p>
|
||||
More than 5 results found.
|
||||
<br />
|
||||
Refine your search if necessary.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='column'>
|
||||
{/* eslint-disable-next-line */}
|
||||
<label>已选组件</label>
|
||||
{get(values, 'dependencies', []).length === 0 ? (
|
||||
<div className='search-no-selected'>暂未选择任何组件</div>
|
||||
) : (
|
||||
<Dependencies
|
||||
list={dependencies}
|
||||
remove={item => {
|
||||
dispatch({
|
||||
type: 'REMOVE_DEPENDENCY',
|
||||
payload: { id: item.id },
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
QuickSearch.defaultProps = {}
|
||||
|
||||
QuickSearch.propTypes = {
|
||||
submit: PropTypes.func.isRequired,
|
||||
input: PropTypes.oneOfType([
|
||||
PropTypes.func,
|
||||
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
|
||||
]).isRequired,
|
||||
}
|
||||
|
||||
export default QuickSearch
|
||||
@@ -0,0 +1,93 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import {CSSTransition, TransitionGroup} from 'react-transition-group'
|
||||
|
||||
import {IconPlus} from '../../icons'
|
||||
|
||||
function Result({ list, selected, select, add }) {
|
||||
return (
|
||||
<TransitionGroup component='ul' className='dependencies-list'>
|
||||
{list.map((item, index) => {
|
||||
return (
|
||||
<CSSTransition timeout={500} classNames='fade' key={`f${item.id}`}>
|
||||
<li>
|
||||
<span className='dependency-item-span'>
|
||||
<a
|
||||
href='/'
|
||||
className={`dependency-item dependency-item-gray ${
|
||||
!item.valid ? 'invalid' : ''
|
||||
} ${selected === index ? 'selected' : ''}`}
|
||||
key={`item${item.id}`}
|
||||
selected={selected === index}
|
||||
disabled={!item.valid}
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
if (item.valid) {
|
||||
add(item)
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
if (item.valid) {
|
||||
select(index)
|
||||
}
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (item.valid) {
|
||||
select(-1)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<strong className='title' key={`item${item.id}`}>
|
||||
{item.name}
|
||||
</strong>
|
||||
<br />
|
||||
{item.valid && (
|
||||
<span key={`d1${item.id}`}>
|
||||
<span className='description' key={`d2${item.id}`}>
|
||||
{item.description}
|
||||
</span>
|
||||
<span key={`d3${item.id}`} className='icon'>
|
||||
<IconPlus key={`d4${item.id}`} />
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
{!item.valid && (
|
||||
<span className='warning' key={`warning${item.id}`}>
|
||||
{item.message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
</span>
|
||||
</li>
|
||||
</CSSTransition>
|
||||
)
|
||||
})}
|
||||
</TransitionGroup>
|
||||
)
|
||||
// }
|
||||
}
|
||||
|
||||
Result.defaultProps = {
|
||||
list: [],
|
||||
selected: null,
|
||||
}
|
||||
|
||||
Result.propTypes = {
|
||||
list: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
group: PropTypes.string.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
versionRange: PropTypes.string,
|
||||
versionRequirement: PropTypes.string,
|
||||
})
|
||||
),
|
||||
add: PropTypes.func.isRequired,
|
||||
selected: PropTypes.number,
|
||||
select: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default Result
|
||||
82
initializer-page/src/components/common/explore/Code.js
Normal file
82
initializer-page/src/components/common/explore/Code.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import Highlight from 'prism-react-renderer'
|
||||
import Prism from 'prism-react-renderer/prism'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import get from 'lodash.get'
|
||||
|
||||
const ReactMarkdown = require('react-markdown')
|
||||
|
||||
if (typeof global !== 'undefined') {
|
||||
global.Prism = Prism
|
||||
require('prismjs/components/prism-java') // eslint-disable-line
|
||||
require('prismjs/components/prism-kotlin') // eslint-disable-line
|
||||
require('prismjs/components/prism-properties') // eslint-disable-line
|
||||
require('prismjs/components/prism-groovy') // eslint-disable-line
|
||||
require('prismjs/components/prism-git') // eslint-disable-line
|
||||
}
|
||||
|
||||
function Code({ item }) {
|
||||
const code = get(item, 'content', '').replace(/\t/g, ' ')
|
||||
const language = get(item, 'language')
|
||||
if (language === 'markdown' && !get(item, 'force', false)) {
|
||||
return (
|
||||
<div className='markdown'>
|
||||
<ReactMarkdown linkTarget='_blank' source={code} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Highlight Prism={Prism} code={code} language={language} theme={null}>
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => {
|
||||
let groupLine = tokens.length > 9 ? '2' : '1'
|
||||
groupLine = tokens.length > 99 ? '3' : groupLine
|
||||
groupLine = tokens.length > 999 ? '4' : groupLine
|
||||
return (
|
||||
<pre className={`${className} line-${groupLine}`} style={style}>
|
||||
{tokens.map((line, i) => {
|
||||
const props = getLineProps({ line, key: i })
|
||||
return (
|
||||
<div
|
||||
key={get(props, 'key')}
|
||||
className={get(props, 'className')}
|
||||
>
|
||||
<span data-value={i + 1} className='explorer-number' />
|
||||
{line.map((token, key) => {
|
||||
const props2 = getTokenProps({ token, key })
|
||||
return (
|
||||
<span
|
||||
className={get(props2, 'className')}
|
||||
style={get(props2, 'style')}
|
||||
key={get(props2, 'key')}
|
||||
>
|
||||
{get(props2, 'children')}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</pre>
|
||||
)
|
||||
}}
|
||||
</Highlight>
|
||||
)
|
||||
}
|
||||
|
||||
Code.defaultProps = {
|
||||
item: {
|
||||
content: '',
|
||||
force: false,
|
||||
language: 'md',
|
||||
},
|
||||
}
|
||||
|
||||
Code.propTypes = {
|
||||
item: PropTypes.shape({
|
||||
content: PropTypes.string,
|
||||
force: PropTypes.bool,
|
||||
language: PropTypes.string,
|
||||
}),
|
||||
}
|
||||
|
||||
export default Code
|
||||
199
initializer-page/src/components/common/explore/Explore.js
Normal file
199
initializer-page/src/components/common/explore/Explore.js
Normal file
@@ -0,0 +1,199 @@
|
||||
import '../../../styles/explore.scss'
|
||||
|
||||
import FileSaver from 'file-saver'
|
||||
import JSZip from 'jszip'
|
||||
import Modal from 'react-responsive-modal'
|
||||
import PropTypes from 'prop-types'
|
||||
import get from 'lodash.get'
|
||||
import React, {useContext, useEffect, useState} from 'react'
|
||||
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
||||
import {toast} from 'react-toastify'
|
||||
|
||||
import Code from './Code'
|
||||
import Loading from './Loading'
|
||||
import Tree from './Tree'
|
||||
import {AppContext} from '../../reducer/App'
|
||||
import {IconFile, IconTimes} from '../icons'
|
||||
import {createTree, findRoot} from '../../utils/Zip'
|
||||
|
||||
function Explore({ open, onClose, projectName, blob }) {
|
||||
const [button, setButton] = useState('Copy')
|
||||
const [tree, setTree] = useState(null)
|
||||
const [selected, setSelected] = useState(null)
|
||||
const { dispatch, explore } = useContext(AppContext)
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
try {
|
||||
const zipJs = new JSZip()
|
||||
const { files } = await zipJs.loadAsync(blob).catch(() => {
|
||||
throw Error(`Could not load the ZIP project.`)
|
||||
})
|
||||
const path = `${findRoot({ files })}/`
|
||||
const result = await createTree(files, path, path, zipJs).catch(() => {
|
||||
throw Error(`Could not read the ZIP project.`)
|
||||
})
|
||||
setSelected(result.selected)
|
||||
setTree(result.tree)
|
||||
} catch (e) {
|
||||
dispatch({ type: 'EXPLORE_UPDATE', payload: { open: false } })
|
||||
toast.error(e.message)
|
||||
}
|
||||
}
|
||||
if (explore && blob) {
|
||||
load()
|
||||
}
|
||||
}, [explore, blob, dispatch])
|
||||
|
||||
const onCopy = () => {
|
||||
setButton('Copied!')
|
||||
setTimeout(() => {
|
||||
setButton('Copy!')
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
const download = file => {
|
||||
const blobFile = new Blob([file.content], {
|
||||
type: 'text/plain;charset=utf-8',
|
||||
})
|
||||
FileSaver.saveAs(blobFile, file.filename)
|
||||
}
|
||||
|
||||
const downloadZip = () => {
|
||||
FileSaver.saveAs(blob, projectName)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => {
|
||||
setSelected(null)
|
||||
onClose()
|
||||
}}
|
||||
showCloseIcon={false}
|
||||
classNames={{ modal: 'modal-explorer', overlay: 'overlay' }}
|
||||
>
|
||||
{tree && selected ? (
|
||||
<div className='colset-explorer'>
|
||||
<div className='left'>
|
||||
<div className='head'>
|
||||
<strong>{projectName}</strong>
|
||||
</div>
|
||||
<div className='explorer-content'>
|
||||
<Tree
|
||||
selected={selected}
|
||||
onClickItem={item => {
|
||||
setSelected(item)
|
||||
// onSelected(item)
|
||||
}}
|
||||
tree={tree}
|
||||
/>
|
||||
</div>
|
||||
<div className='foot'>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
downloadZip()
|
||||
}}
|
||||
className='action'
|
||||
>
|
||||
下载源码包
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className='right'>
|
||||
{selected && (
|
||||
<>
|
||||
<div className='head'>
|
||||
<strong>
|
||||
<IconFile />
|
||||
{get(selected, 'filename')}
|
||||
</strong>
|
||||
<div className='actions'>
|
||||
<span className='divider'>|</span>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
download(selected)
|
||||
}}
|
||||
className='action'
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
<span className='divider'>|</span>
|
||||
<CopyToClipboard
|
||||
onCopy={onCopy}
|
||||
text={get(selected, 'content', '')}
|
||||
>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
className='action'
|
||||
>
|
||||
{button}
|
||||
</a>
|
||||
</CopyToClipboard>
|
||||
{get(selected, 'language') === 'markdown' && (
|
||||
<>
|
||||
<span className='divider'>|</span>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
const newSelected = { ...selected }
|
||||
newSelected.force = !get(selected, 'force', false)
|
||||
setSelected(newSelected)
|
||||
}}
|
||||
className='action'
|
||||
>
|
||||
{get(selected, 'force', false)
|
||||
? 'Preview'
|
||||
: 'View source'}
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
onClose()
|
||||
}}
|
||||
className='close'
|
||||
>
|
||||
<IconTimes />
|
||||
</a>
|
||||
</div>
|
||||
<div className='explorer-content'>
|
||||
<Code item={selected} onChange={() => {}} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Loading onClose={onClose} />
|
||||
)}
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Explore.defaultProps = {
|
||||
projectName: '',
|
||||
blob: null,
|
||||
}
|
||||
|
||||
Explore.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
projectName: PropTypes.string,
|
||||
blob: PropTypes.instanceOf(Blob),
|
||||
}
|
||||
|
||||
export default Explore
|
||||
84
initializer-page/src/components/common/explore/Loading.js
Normal file
84
initializer-page/src/components/common/explore/Loading.js
Normal file
@@ -0,0 +1,84 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
import {IconTimes} from '../icons'
|
||||
import {Placeholder} from '../form'
|
||||
|
||||
function Loading({ onClose }) {
|
||||
return (
|
||||
<>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
</a>
|
||||
<div className='colset-explorer'>
|
||||
<div className='left'>
|
||||
<div className='head'>
|
||||
<Placeholder width='70px' type='text' />
|
||||
</div>
|
||||
<div className='explorer-content'>
|
||||
<ul className='explorer-ul-placeholder'>
|
||||
<li>
|
||||
<Placeholder type='text' width='66px' />
|
||||
</li>
|
||||
<li>
|
||||
<Placeholder type='text' width='60px' />
|
||||
</li>
|
||||
<li>
|
||||
<Placeholder type='text' width='45px' />
|
||||
</li>
|
||||
<li>
|
||||
<Placeholder type='text' width='87px' />
|
||||
</li>
|
||||
<li>
|
||||
<Placeholder type='text' width='80px' />
|
||||
</li>
|
||||
<li>
|
||||
<Placeholder type='text' width='94px' />
|
||||
</li>
|
||||
<li>
|
||||
<Placeholder type='text' width='86px' />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className='foot'>
|
||||
<Placeholder type='text' width='120px' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='right'>
|
||||
<>
|
||||
<div className='head'>
|
||||
<Placeholder width='86px' type='text' />
|
||||
<div className='actions'>
|
||||
<Placeholder width='74px' type='text' />
|
||||
<Placeholder width='43px' type='text' />
|
||||
</div>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
onClose()
|
||||
}}
|
||||
className='close'
|
||||
>
|
||||
<IconTimes />
|
||||
</a>
|
||||
</div>
|
||||
<div className='content' />
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Loading.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default Loading
|
||||
122
initializer-page/src/components/common/explore/Tree.js
Normal file
122
initializer-page/src/components/common/explore/Tree.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import get from 'lodash.get'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
|
||||
import {IconCaretDown, IconFile, IconFolder} from '../icons'
|
||||
|
||||
function Tree({ tree, selected, onClickItem }) {
|
||||
const [folders, setFolders] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
const treeToArray = map => {
|
||||
const recursive = (mapRec, acc) => {
|
||||
mapRec.forEach(item => {
|
||||
if (item.type === 'folder') {
|
||||
acc.push({
|
||||
filename: get(item, 'filename'),
|
||||
path: get(item, 'path'),
|
||||
hidden: get(item, 'hidden', false),
|
||||
})
|
||||
if (get(item, 'children')) {
|
||||
recursive(item.children, acc)
|
||||
}
|
||||
}
|
||||
})
|
||||
return acc
|
||||
}
|
||||
return recursive(map, [])
|
||||
}
|
||||
setFolders(treeToArray(tree.children))
|
||||
}, [tree, setFolders])
|
||||
|
||||
const renderItem = (item, depth = 0) => {
|
||||
if (item.type === 'folder') {
|
||||
const folder = folders.find(f => f.path === item.path)
|
||||
const isHidden = get(folder, `hidden`, true)
|
||||
return (
|
||||
<li
|
||||
key={`li${item.path}`}
|
||||
className={`li-folder ${isHidden ? 'folder-hide' : ''}`}
|
||||
>
|
||||
<a
|
||||
href='/#'
|
||||
key={`s1${item.path}`}
|
||||
className={`folder level-${depth}`}
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
const newFolders = [...folders]
|
||||
const newFolder = newFolders.find(f => f.path === item.path)
|
||||
if (newFolder) {
|
||||
newFolder.hidden = !get(newFolder, `hidden`, true)
|
||||
setFolders(newFolders)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span key={`s2${item.path}`} className='text'>
|
||||
{get(item, 'children.length', 0) > 0 && <IconCaretDown />}
|
||||
<span key={`s3${item.path}`} className='icon'>
|
||||
<IconFolder key={`s4${item.path}`} />
|
||||
</span>
|
||||
{item.filename}
|
||||
</span>
|
||||
</a>
|
||||
{get(item, 'children') && (
|
||||
<ul className='ul' key={`ul${item.path}`}>
|
||||
{item.children.map(it => renderItem(it, depth + 1))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
// File
|
||||
const isDisabled = get(item, 'language') === null
|
||||
const isSelected = get(selected, 'path') === get(item, 'path')
|
||||
return (
|
||||
<li key={`li${item.path}`} className='li-file'>
|
||||
<a
|
||||
href='/#'
|
||||
key={`s1${item.path}`}
|
||||
tabIndex={`${isDisabled ? -1 : ''}`}
|
||||
className={`file level-${depth} ${isDisabled ? 'disabled' : ''} ${
|
||||
isSelected ? 'selected' : ''
|
||||
}`}
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
if (!isDisabled) {
|
||||
onClickItem(item)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span key={`s2${item.path}`} className='text'>
|
||||
<span key={`s3${item.path}`} className='icon'>
|
||||
<IconFile key={`s4${item.path}`} />
|
||||
</span>
|
||||
{item.filename}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className='explorer-ul'>
|
||||
{tree.children.map(item => renderItem(item, 0))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
Tree.propTypes = {
|
||||
tree: PropTypes.shape({
|
||||
children: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
type: PropTypes.string,
|
||||
})
|
||||
),
|
||||
}).isRequired,
|
||||
selected: PropTypes.shape({
|
||||
path: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
onClickItem: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default Tree
|
||||
1
initializer-page/src/components/common/explore/index.js
Normal file
1
initializer-page/src/components/common/explore/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Explore } from './Explore'
|
||||
100
initializer-page/src/components/common/fetch/Fetch.js
Normal file
100
initializer-page/src/components/common/fetch/Fetch.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import '../../../styles/fetch.scss'
|
||||
|
||||
import get from 'lodash.get'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {useContext, useRef, useState,} from 'react'
|
||||
import {toast} from 'react-toastify'
|
||||
import Modal from 'react-responsive-modal'
|
||||
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
||||
import FileSaver from 'file-saver'
|
||||
import useWindowsUtils from '../../utils/WindowsUtils'
|
||||
import {InitializrContext} from '../../reducer/Initializr'
|
||||
import {getProject, getQueryString} from '../../utils/ApiUtils'
|
||||
import {AppContext} from '../../reducer/App'
|
||||
|
||||
function Fetch({ open, onClose }) {
|
||||
const windowsUtils = useWindowsUtils()
|
||||
const origin = `${windowsUtils.origin}`
|
||||
const { values } = useContext(InitializrContext)
|
||||
const { dependencies } = useContext(AppContext)
|
||||
|
||||
const config = get(dependencies, 'list')
|
||||
|
||||
const params = getQueryString(values, config)
|
||||
|
||||
const gitUrl = 'git clone "' + `${origin}/${params}/${values.meta.artifact}.git` + '" ' + `${values.meta.artifact}`
|
||||
const downloadUrl = `${origin}/starter.zip?${params}`
|
||||
|
||||
const [button, setButton] = useState('复制')
|
||||
const input = useRef(null)
|
||||
const onCopy = () => {
|
||||
setButton('已复制!')
|
||||
setTimeout(() => {
|
||||
onClose()
|
||||
setButton('复制')
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const onDownload = async () => {
|
||||
const url = `${windowsUtils.origin}/starter.zip`
|
||||
const project = await getProject(
|
||||
url,
|
||||
values,
|
||||
get(dependencies, 'list')
|
||||
).catch(() => {
|
||||
toast.error(`Could not connect to server. Please check your network.`)
|
||||
})
|
||||
FileSaver.saveAs(project, `${get(values, 'meta.artifact')}.zip`)
|
||||
onClose()
|
||||
setButton('复制')
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => {
|
||||
onClose()
|
||||
setButton('复制')
|
||||
}}
|
||||
classNames={{ modal: 'modal-fetch', overlay: 'overlay' }}
|
||||
center
|
||||
>
|
||||
<div>
|
||||
<h2>获取完整项目代码</h2>
|
||||
<div>
|
||||
<div class="fetch-method control">
|
||||
<span>下载代码包:</span>
|
||||
<input class="control-input" value={downloadUrl} readonly />
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
onDownload()
|
||||
}}
|
||||
>下载</a>
|
||||
</div>
|
||||
<div class="fetch-method control">
|
||||
<span>Git Clone 命令:</span>
|
||||
<input class="control-input" value={gitUrl} readonly />
|
||||
<CopyToClipboard onCopy={onCopy} text={gitUrl}>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
>{button}</a>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Fetch.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default Fetch
|
||||
42
initializer-page/src/components/common/form/Button.js
Normal file
42
initializer-page/src/components/common/form/Button.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
function Button({ id, onClick, children, variant, hotkey }) {
|
||||
return (
|
||||
<button
|
||||
className={`button ${variant === 'primary' ? 'primary' : ''}`}
|
||||
type='button'
|
||||
id={id}
|
||||
onClick={event => {
|
||||
if (onClick) {
|
||||
onClick(event)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
{hotkey && (
|
||||
<>
|
||||
{' '}
|
||||
<span className='desktop-only'>- {hotkey}</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
Button.defaultProps = {
|
||||
onClick: null,
|
||||
children: null,
|
||||
variant: '',
|
||||
hotkey: '',
|
||||
}
|
||||
|
||||
Button.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
variant: PropTypes.string,
|
||||
hotkey: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
export default Button
|
||||
29
initializer-page/src/components/common/form/Close.js
Normal file
29
initializer-page/src/components/common/form/Close.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
import {IconTimes} from '../icons'
|
||||
|
||||
const Close = ({ onClose }) => (
|
||||
<a
|
||||
href='/#'
|
||||
className='toast-close'
|
||||
onClick={event => {
|
||||
event.preventDefault()
|
||||
if (onClose) {
|
||||
onClose()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconTimes />
|
||||
</a>
|
||||
)
|
||||
|
||||
Close.defaultProps = {
|
||||
onClose: null,
|
||||
}
|
||||
|
||||
Close.propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
}
|
||||
|
||||
export default Close
|
||||
29
initializer-page/src/components/common/form/Form.js
Normal file
29
initializer-page/src/components/common/form/Form.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
const Form = ({ onSubmit, children }) => (
|
||||
<form onSubmit={onSubmit} autoComplete='off'>
|
||||
<input
|
||||
style={{ display: 'none' }}
|
||||
type='text'
|
||||
name='fakeusernameremembered'
|
||||
/>
|
||||
<input
|
||||
style={{ display: 'none' }}
|
||||
type='password'
|
||||
name='fakepasswordremembered'
|
||||
/>
|
||||
{children}
|
||||
</form>
|
||||
)
|
||||
|
||||
Form.defaultProps = {
|
||||
children: null,
|
||||
}
|
||||
|
||||
Form.propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
export default Form
|
||||
49
initializer-page/src/components/common/form/Placeholder.js
Normal file
49
initializer-page/src/components/common/form/Placeholder.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
function Placeholder({ type, count, width }) {
|
||||
if (type === 'radios') {
|
||||
const options = Array.from({ length: count }, (item, i) => {
|
||||
const id = `p${i}`
|
||||
return <span key={id} style={{ width }} className='placeholder-radio' />
|
||||
})
|
||||
return <span className='placeholder-radios'>{options}</span>
|
||||
}
|
||||
if (type === 'input') {
|
||||
return <span className='placeholder-input' />
|
||||
}
|
||||
if (type === 'dropdown') {
|
||||
return <span className='placeholder-dropdown' />
|
||||
}
|
||||
if (type === 'text') {
|
||||
return <span style={{ width }} className='placeholder-text' />
|
||||
}
|
||||
if (type === 'button') {
|
||||
return <span style={{ width }} className='placeholder-button' />
|
||||
}
|
||||
if (type === 'tabs') {
|
||||
const options = Array.from({ length: count }, (item, i) => {
|
||||
return <span key={`p${i}`} className='placeholder-tab' />
|
||||
})
|
||||
return (
|
||||
<span className='placeholder-tabs'>
|
||||
<span className='placeholder-header'>{options}</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
Placeholder.defaultProps = {
|
||||
width: '150px',
|
||||
type: 'radios',
|
||||
count: 3,
|
||||
}
|
||||
|
||||
Placeholder.propTypes = {
|
||||
type: PropTypes.string,
|
||||
count: PropTypes.number,
|
||||
width: PropTypes.string,
|
||||
}
|
||||
|
||||
export default Placeholder
|
||||
45
initializer-page/src/components/common/form/Radio.js
Normal file
45
initializer-page/src/components/common/form/Radio.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
function Radio({ handler, value, disabled, error, checked, text }) {
|
||||
const onClick = event => {
|
||||
event.preventDefault()
|
||||
handler(value)
|
||||
}
|
||||
if (disabled || error) {
|
||||
return (
|
||||
<span
|
||||
className={`radio disabled ${checked ? 'checked' : ''} ${
|
||||
error ? 'err' : ''
|
||||
}`}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<a
|
||||
href='/'
|
||||
className={`radio ${checked ? 'checked' : ''}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
Radio.defaultProps = {
|
||||
disabled: false,
|
||||
error: false,
|
||||
}
|
||||
|
||||
Radio.propTypes = {
|
||||
checked: PropTypes.bool.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
handler: PropTypes.func.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
error: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default Radio
|
||||
63
initializer-page/src/components/common/form/RadioGroup.js
Normal file
63
initializer-page/src/components/common/form/RadioGroup.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
import Radio from './Radio'
|
||||
|
||||
function RadioGroup({ onChange, options, error, selected, disabled }) {
|
||||
const onChangeHandler = value => {
|
||||
if (onChange) {
|
||||
onChange(value)
|
||||
}
|
||||
}
|
||||
const allOptions = options.map(option => {
|
||||
return (
|
||||
<Radio
|
||||
key={option.key}
|
||||
checked={!error && selected === option.key}
|
||||
text={option.text}
|
||||
value={option.key}
|
||||
disabled={disabled}
|
||||
handler={onChangeHandler}
|
||||
/>
|
||||
)
|
||||
})
|
||||
if (error) {
|
||||
allOptions.push(
|
||||
<Radio
|
||||
key={allOptions.length + 1}
|
||||
checked
|
||||
text={error}
|
||||
value={error}
|
||||
disabled={disabled}
|
||||
handler={onChangeHandler}
|
||||
error
|
||||
/>
|
||||
)
|
||||
}
|
||||
return <div className='group-radio'>{allOptions}</div>
|
||||
}
|
||||
|
||||
RadioGroup.defaultProps = {
|
||||
selected: '',
|
||||
error: null,
|
||||
onChange: null,
|
||||
disabled: false,
|
||||
options: {
|
||||
error: '',
|
||||
},
|
||||
}
|
||||
|
||||
RadioGroup.propTypes = {
|
||||
selected: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
key: PropTypes.string.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
})
|
||||
),
|
||||
onChange: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default RadioGroup
|
||||
33
initializer-page/src/components/common/form/Switch.js
Normal file
33
initializer-page/src/components/common/form/Switch.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
const Switch = ({ id, isOn, onChange }) => {
|
||||
return (
|
||||
<span className='switch'>
|
||||
<input
|
||||
checked={isOn}
|
||||
onChange={onChange}
|
||||
className='switch-checkbox'
|
||||
id={id}
|
||||
name='switch-new'
|
||||
type='checkbox'
|
||||
/>
|
||||
{/* eslint-disable-next-line */}
|
||||
<label className='switch-label' htmlFor={id}>
|
||||
<span className='switch-button' />
|
||||
</label>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
Switch.defaultProps = {
|
||||
isOn: false,
|
||||
onChange: null,
|
||||
}
|
||||
|
||||
Switch.propTypes = {
|
||||
isOn: PropTypes.bool,
|
||||
onChange: PropTypes.func,
|
||||
}
|
||||
|
||||
export default Switch
|
||||
7
initializer-page/src/components/common/form/index.js
Normal file
7
initializer-page/src/components/common/form/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export { default as Radio } from './Radio'
|
||||
export { default as RadioGroup } from './RadioGroup'
|
||||
export { default as Switch } from './Switch'
|
||||
export { default as Close } from './Close'
|
||||
export { default as Placeholder } from './Placeholder'
|
||||
export { default as Form } from './Form'
|
||||
export { default as Button } from './Button'
|
||||
22
initializer-page/src/components/common/icons/IconSpring.js
Normal file
22
initializer-page/src/components/common/icons/IconSpring.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
|
||||
export const IconSpring = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='spring'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
className='icon-spring'
|
||||
viewBox='0 0 248.7 248.38'
|
||||
>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d='M226.28,14.15a114.05,114.05,0,0,1-13.22,23.44A124.1,124.1,0,1,0,39.75,215.14l2.72,2.4A10.61,10.61,0,1,1,57.39,216a10.62,10.62,0,0,1-14.92,1.55l1.85,1.64a123.56,123.56,0,0,0,79.86,29.19c65.45,0,119.21-50.94,123.81-115.24C251.4,101.66,242.1,61.69,226.28,14.15Zm-.53,164.69c-30.61,40.78-96,27-137.92,29,0,0-7.42.42-14.89,1.65,0,0,2.83-1.21,6.43-2.45,29.44-10.19,43.35-12.23,61.25-21.42,33.61-17.22,67.06-54.72,73.87-93.69-12.8,37.48-51.71,69.73-87.14,82.82-24.25,9-68.11,17.66-68.11,17.67s-1.75-.92-1.78-.94C27.64,177,26.78,112.36,81,91.54c23.75-9.14,46.47-4.12,72.14-10.23,27.38-6.51,59.1-27,72-53.88C239.53,70.3,256.89,137.36,225.75,178.84Z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
339
initializer-page/src/components/common/icons/Icons.js
Normal file
339
initializer-page/src/components/common/icons/Icons.js
Normal file
@@ -0,0 +1,339 @@
|
||||
import React from 'react'
|
||||
|
||||
export const IconCaretDown = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='caret-down'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 320 512'
|
||||
className='icon-caret-down'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconCheck = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='check'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 512 512'
|
||||
className='icon-check'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconFolder = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='folder'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
className='icon-folder'
|
||||
viewBox='0 0 512 512'
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
fill='#86ad5c'
|
||||
d='M430.1,192H81.9c-17.7,0-18.6,9.2-17.6,20.5l13,183c0.9,11.2,3.5,20.5,21.1,20.5h316.2c18,0,20.1-9.2,21.1-20.5l12.1-185.3 C448.7,199,447.8,192,430.1,192z'
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
fill='#718c50'
|
||||
d='M426.2,143.3c-0.5-12.4-4.5-15.3-15.1-15.3c0,0-121.4,0-143.2,0c-21.8,0-24.4,0.3-40.9-17.4C213.3,95.8,218.7,96,190.4,96 c-22.6,0-75.3,0-75.3,0c-17.4,0-23.6-1.5-25.2,16.6c-1.5,16.7-5,57.2-5.5,63.4h343.4L426.2,143.3z'
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconFile = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='file'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
className='icon-file'
|
||||
viewBox='0 0 1792 1792'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M1596 380q28 28 48 76t20 88v1152q0 40-28 68t-68 28h-1344q-40 0-68-28t-28-68v-1600q0-40 28-68t68-28h896q40 0 88 20t76 48zm-444-244v376h376q-10-29-22-41l-313-313q-12-12-41-22zm384 1528v-1024h-416q-40 0-68-28t-28-68v-416h-768v1536h1280z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconChevronLeft = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='chevron-left'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 320 512'
|
||||
className='icon-chevron-left'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconChevronRight = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='chevron-right'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 320 512'
|
||||
className='icon-chevron-right'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconChevronDown = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='chevron-down'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 448 512'
|
||||
className='icon-chevron-down'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconChevronUp = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='chevron-up'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 448 512'
|
||||
className='icon-chevron-up'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconGithub = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='github'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 496 512'
|
||||
className='icon-github'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconList = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='list'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 512 512'
|
||||
className='icon-list'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M128 116V76c0-8.837 7.163-16 16-16h352c8.837 0 16 7.163 16 16v40c0 8.837-7.163 16-16 16H144c-8.837 0-16-7.163-16-16zm16 176h352c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H144c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h352c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H144c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zM16 144h64c8.837 0 16-7.163 16-16V64c0-8.837-7.163-16-16-16H16C7.163 48 0 55.163 0 64v64c0 8.837 7.163 16 16 16zm0 160h64c8.837 0 16-7.163 16-16v-64c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v64c0 8.837 7.163 16 16 16zm0 160h64c8.837 0 16-7.163 16-16v-64c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v64c0 8.837 7.163 16 16 16z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconPlus = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-prefix='fas'
|
||||
data-icon='plus'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 448 512'
|
||||
className='icon-plus'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconSearch = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='search'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 512 512'
|
||||
className='icon-search'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconTimes = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-prefix='fas'
|
||||
data-icon='times'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 352 512'
|
||||
className='icon-times'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconTwitter = () => (
|
||||
<svg
|
||||
aria-hidden='true'
|
||||
focusable='false'
|
||||
data-icon='twitter'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 512 512'
|
||||
className='icon-twitter'
|
||||
>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconHelp = () => (
|
||||
<svg
|
||||
t="1583993417464"
|
||||
className='icon-help'
|
||||
focusable='false'
|
||||
aria-hidden='true'
|
||||
role='img'
|
||||
data-icon='help'
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="1960"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
p-id="1961"
|
||||
fill='currentColor'
|
||||
d="M0.12992 518.398052C-6.268452 236.869666 224.072954 6.528259 505.601341 0.129887c281.528386-6.398372 511.869793 223.943034 518.268165 505.471421 6.398372 281.528386-223.943034 511.869793-505.47142 518.268165-281.528386 6.398372-511.869793-223.943034-518.268166-505.471421zM595.178555 767.934577c0-44.788607-38.390234-83.178841-83.178842-83.178842-44.788607 0-83.178841 38.390234-83.178841 83.178842 0 44.788607 38.390234 83.178841 83.178841 83.178841 44.788607 0 83.178841-38.390234 83.178842-83.178841zM300.853423 345.641997v12.796745c0 31.991862 6.398372 63.983724 63.983725 63.983724S435.219244 384.032232 435.219244 345.641997c0-12.796745 38.390234-44.788607 76.780469-44.788607s83.178841 19.195117 83.178842 76.780469c0 38.390234-63.983724 76.780469-95.975587 108.772331-70.382097 63.983724-63.983724 89.577214-63.983724 89.577214 0 38.390234 12.796745 76.780469 70.382097 70.382097 57.585352-6.398372 166.357683-153.560938 166.357682-153.560938 51.186979-57.585352 51.186979-127.967448 51.18698-127.967448 0-121.569076-95.975586-185.5528-211.14629-185.5528-115.170703-6.398372-204.747917 51.186979-211.14629 166.357682z">
|
||||
</path>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconFeedBack = () => (
|
||||
<svg
|
||||
t="1589424011678"
|
||||
class="icon-feedback"
|
||||
focusable='false'
|
||||
aria-hidden='true'
|
||||
viewBox="0 0 1070 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="1374"
|
||||
width="32"
|
||||
height="32">
|
||||
<path
|
||||
fill='currentColor'
|
||||
p-id="1375"
|
||||
d="M1021.713308 633.948781a48.653015 48.653015 0 0 1-48.653014-48.653014l-0.389224-487.673493-728.578895 0.413551a48.653015 48.653015 0 0 1 0-97.30603l678.709554-0.389224v0.413551h51.693829a97.306029 97.306029 0 0 1 95.359908 77.844823L1070.366323 585.295767a48.653015 48.653015 0 0 1-48.653015 48.653014zM924.407279 229.544923l0.413551 307.584359c0 1.216325-0.267592 2.432651-0.364898 3.600323l0.291918 200.912624a96.965458 96.965458 0 0 1-96.138357 86.213142h-51.693828l-270.024232 0.316245-182.448805 181.086521a48.653015 48.653015 0 1 1-68.795362-68.795363l189.162921-187.703331a48.653015 48.653015 0 0 1 40.576614-21.893856h1.946121l340.157552-0.316245v-76.506866 10.217133-17.75835L827.10125 486.5788c0-1.289305 0.267592-2.432651 0.364897-3.721956l-0.218938-164.155271c-1.021713-64.07602-9.000808-73.247114-64.659857-74.487766l64.173326 0.437877 0.486531 70.668504L827.10125 244.019195h-71.665891 7.10334-15.495985l-587.314867 0.316245c-51.085665 1.727182-60.645983 10.922602-62.397491 57.434884l0.291918 382.388368c2.432651 38.533188 14.085048 45.101345 65.851855 46.220364l-65.827529-0.437877v0.997387l194.903977-0.340571a48.653015 48.653015 0 0 1 0 97.306029l-143.526393 0.243265v-0.291918H97.306029a97.111417 97.111417 0 0 1-96.892478-93.073217L0 243.484012a97.062764 97.062764 0 0 1 92.927258-96.332969l736.120112-0.437877A96.989785 96.989785 0 0 1 924.407279 229.544923z m-827.10125 14.912149v33.424621l0.218939-33.230009 31.259562-0.218938H97.306029z m669.708747 485.873331c51.085665-1.240652 59.040433-9.730603 60.18378-65.997814l-0.462204 65.68157z m0 0c-10.460398 0.243265-22.502019 0.243265-36.927638 0.243265z m-603.51632 0l32.281275 0.218939c-12.211907 0-22.84259 0-32.232622-0.194612z m741.958474-632.099966l65.803202 0.437877 0.364898 54.613009c-1.483917-46.512282-10.72799-53.90754-66.192427-55.02656z m0 0l-32.402908-0.170286c12.284886 0 22.939896 0 32.378581 0.194612z m66.435691 97.135743l-0.291918-42.036204c0.364898 11.482111 0.291918 25.202262 0.291918 42.036204z">
|
||||
</path>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconCloudTookit = () => (
|
||||
<svg
|
||||
t="1587979013746"
|
||||
className='icon-cloud-tookit'
|
||||
focusable='false'
|
||||
aria-hidden='true'
|
||||
role='img'
|
||||
data-icon='help'
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="679"
|
||||
width="25"
|
||||
height="25">
|
||||
<path
|
||||
p-id="680"
|
||||
fill='currentColor'
|
||||
d="M228.616 662.377c-18.254-4.236-32.293-19.763-32.293-39.526V405.455c1.404-19.764 14.041-35.291 32.293-39.527l200.78-43.76 21.062-86.112H217.384c-82.839 0-148.83 66.347-148.83 148.225v256.923c0 81.876 67.395 149.636 148.83 149.636h233.074l-21.061-86.112-200.781-42.351z m584.092-426.323H578.226l21.062 86.111 200.785 43.761c18.253 4.237 32.287 19.764 32.287 39.526V622.85c-1.4 19.763-14.035 35.29-32.287 39.526l-200.785 43.762-21.062 86.11h234.482c81.436 0 148.83-66.347 148.83-149.635V384.278c-1.41-81.877-67.394-148.224-148.83-148.224zM429.396 502.86H600.69v21.174H429.396V502.86z">
|
||||
</path>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const IconInitialzrTutorial = () => (
|
||||
<svg t="1603192889200" class="icon-cloud-tookit" viewBox="0 0 1376 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="1399" width="200" height="200" style={{left: '20px'}}>
|
||||
<path
|
||||
d="M596.800316 1023.986434q-3.66285 0-7.447794-0.217058c-24.581792-1.356611-50.303136-9.048595-80.04005-17.934397-110.238211-32.789288-153.134251-29.397761-187.565038-26.630275-51.008574 4.069833-91.842566 15.926613-127.88772 26.345386-56.177262 16.279332-104.689672 30.293124-147.029502-6.783055C18.802629 974.239508 3.065941 934.6129 0.054264 880.972501V186.04851c0-91.313487 68.49529-165.750733 153.161384-167.093778a1093.645533 1093.645533 0 0 1 405.463899 0 151.831904 151.831904 0 0 1 108.094765 49.774058c28.990777 31.554772 44.958089 73.256995 44.958089 117.31972V879.683721c-3.147338 53.002792-18.870459 92.819325-46.748815 118.36431-19.00612 17.418885-41.403768 25.938403-68.18327 25.938403z m-243.376015-93.60616c69.187162 0 124.021379 16.360729 169.400017 29.845442 57.561005 17.161129 86.294026 24.418998 110.034719 2.713222 18.354947-16.821977 28.922947-45.58213 31.378413-85.466493V186.04851c0-32.165247-11.517627-62.404107-32.43657-85.195171a105.476506 105.476506 0 0 0-75.631063-34.430788h-2.19771l-2.157012-0.406983a1046.245545 1046.245545 0 0 0-391.884223 0l-2.19771 0.420549h-2.238408c-28.326038 0-55.159804 12.209499-75.563233 34.444354-20.918942 22.763933-32.436569 53.016358-32.436569 85.195171v692.943339c2.333371 40.060723 12.630049 68.346063 30.591578 84.109883 10.337376 9.048595 21.488718 12.52152 37.320369 11.626156 17.635943-1.003892 39.72157-7.379964 65.239423-14.759928 36.302911-10.513735 81.491623-23.591465 137.275469-28.068281 12.236631-1.031024 24.066279-1.478706 35.516076-1.478706z"
|
||||
p-id="1400" fill='currentColor'></path>
|
||||
<path
|
||||
d="M1261.064898 1023.986434c-2.4419 0-4.924498-0.067831-7.447794-0.217058-24.581792-1.356611-50.303136-9.048595-80.04005-17.934397-110.251777-32.789288-153.174949-29.397761-187.605737-26.630275-51.008574 4.069833-91.842566 15.926613-127.88772 26.345386-56.177262 16.279332-104.689672 30.293124-147.029501-6.783055-28.027584-24.527527-43.764271-64.154135-46.775948-117.794534l47.413555-2.713222c2.26554 40.521971 12.575784 69.010802 30.605144 84.828887 10.337376 9.048595 21.502285 12.52152 37.320369 11.626156 17.635943-1.003892 39.72157-7.379964 65.239424-14.759928 36.302911-10.513735 81.491623-23.591465 137.275468-28.068282 86.199064-6.932282 152.02183 12.697879 204.916094 28.488832 57.561005 17.161129 86.294026 24.418998 110.034719 2.713222 18.354947-16.821977 28.922947-45.58213 31.378412-85.466494V186.04851c0-32.165247-11.517627-62.404107-32.436569-85.195171a105.476506 105.476506 0 0 0-75.64463-34.430788h-2.19771l-2.157011-0.406983a1046.245545 1046.245545 0 0 0-391.884223 0l-2.19771 0.420549h-2.238408c-28.326038 0-55.159804 12.209499-75.563234 34.444354-20.918942 22.763933-32.436569 53.016358-32.436569 85.195171h-47.481385c0-91.313487 68.49529-165.750733 153.161383-167.093778a1093.645533 1093.645533 0 0 1 405.463899 0 151.831904 151.831904 0 0 1 108.216861 49.746926c28.990777 31.554772 44.958089 73.256995 44.958088 117.31972V879.683721c-3.147338 53.002792-18.870459 92.819325-46.748815 118.36431-19.087517 17.418885-41.444466 25.938403-68.210402 25.938403zM121.362421 229.43293h334.974391v47.481386H121.362421zM121.362421 384.507135h334.974391v47.481386H121.362421z"
|
||||
p-id="1401" fill='currentColor'></path>
|
||||
<path
|
||||
d="M942.342707 431.988521a23.740693 23.740693 0 0 1-23.740693-23.740693V14.681407h47.481386v342.788471l60.626946-50.655856a23.740693 23.740693 0 0 1 30.442351 0l55.376862 46.260436V23.689304h47.481385V403.87954a23.740693 23.740693 0 0 1-38.961868 18.219286l-79.131121-66.12122-84.367638 70.543772a23.740693 23.740693 0 0 1-15.20761 5.467143z"
|
||||
p-id="1402" fill='currentColor'></path>
|
||||
</svg>
|
||||
)
|
||||
export const IconHot = () => (
|
||||
<svg t="1603192889200" class="icon-cloud-tookit" viewBox="0 0 1376 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1399" width="200" height="200">
|
||||
p-id="2096" width="200" height="200">
|
||||
<path
|
||||
d="M498.463465 545.489466c-32.862322 0-49.168055 24.584027-49.168055 61.460069 0 41.015189 16.431161 61.460069 49.168055 61.460068 32.862322 0 49.168055-20.44488 49.168055-61.460068-4.013719-36.876041-16.305732-57.44635-49.168055-61.460069z m0 0"
|
||||
fill="#FF0000" p-id="2097"></path>
|
||||
<path
|
||||
d="M674.816233 164.060755c20.44488 49.168055 24.584027 123.045566-16.431161 164.060754C588.646709 57.44635 412.29394 0 412.29394 0c20.570309 139.476727-73.752082 291.245468-168.074473 406.012739-4.139147-57.44635-8.152866-94.322391-36.876041-147.629593-8.152866 102.475257-82.030377 184.505634-106.614405 282.967172-16.431161 86.169525-4.139147 164.060755 57.320921 233.799119 98.461538 94.322391 352.705537 213.228809 611.088682 4.139147 237.812837-242.077413-36.876041-574.212641-94.322391-615.227829zM379.557047 705.411073h-45.154337v-77.89123H252.372333V705.411073h-45.154336V512.627144h45.154336v77.891229h86.169525v-77.891229h45.154336V705.411073h-4.139147z m118.906418 4.139147c-61.460069-4.139147-94.322391-36.876041-98.461538-98.461538 4.139147-65.599216 36.876041-98.461538 98.461538-102.475257 61.460069 4.139147 90.183244 36.876041 94.322391 102.475257 0 61.460069-32.862322 94.322391-94.322391 98.461538z m221.507104-160.047035v159.921607h-45.154336V549.503185h-61.460068v-36.876041H781.556067v36.876041h-61.585498z m0 0"
|
||||
fill="#FF0000" p-id="2098"></path>
|
||||
</svg>
|
||||
)
|
||||
21
initializer-page/src/components/common/icons/index.js
Normal file
21
initializer-page/src/components/common/icons/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
export { IconTimes } from './Icons'
|
||||
export { IconPlus } from './Icons'
|
||||
export { IconGithub } from './Icons'
|
||||
export { IconTwitter } from './Icons'
|
||||
export { IconCaretDown } from './Icons'
|
||||
export { IconList } from './Icons'
|
||||
export { IconSearch } from './Icons'
|
||||
export { IconCheck } from './Icons'
|
||||
export { IconChevronLeft } from './Icons'
|
||||
export { IconChevronRight } from './Icons'
|
||||
export { IconChevronUp } from './Icons'
|
||||
export { IconChevronDown } from './Icons'
|
||||
export { IconFolder } from './Icons'
|
||||
export { IconFile } from './Icons'
|
||||
export { IconHelp } from './Icons'
|
||||
export { IconCloudTookit } from './Icons'
|
||||
export { IconInitialzrTutorial } from './Icons'
|
||||
export { IconFeedBack } from './Icons'
|
||||
export { IconHot } from './Icons'
|
||||
|
||||
export { IconSpring } from './IconSpring'
|
||||
44
initializer-page/src/components/common/layout/Footer.js
Normal file
44
initializer-page/src/components/common/layout/Footer.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
const Footer = ({ children }) => (
|
||||
<div className='sticky'>
|
||||
<div className='colset colset-submit'>
|
||||
<div className='left nopadding'>
|
||||
<footer className='footer'>
|
||||
<div className='footer-container'>
|
||||
© 1999-{new Date().getFullYear()} Aliyun.com
|
||||
<br />
|
||||
start.aliyun.com is powered by
|
||||
<br />
|
||||
<span>
|
||||
<a
|
||||
tabIndex='-1'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
href='https://www.aliyun.com'
|
||||
>
|
||||
Aliyun.com
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
{children && (
|
||||
<div className='right nopadding'>
|
||||
<div className='submit'>{children}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Footer.defaultProps = {
|
||||
children: null,
|
||||
}
|
||||
|
||||
Footer.propTypes = {
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
export default Footer
|
||||
26
initializer-page/src/components/common/layout/Header.js
Normal file
26
initializer-page/src/components/common/layout/Header.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
const Header = ({ children }) => (
|
||||
<div className='header'>
|
||||
<h1 className='logo'>
|
||||
<a href='/bootstrap.html'>
|
||||
<span className='title'>
|
||||
<strong>云原生</strong>应用脚手架
|
||||
</span>
|
||||
<span className='description'>Cloud Native App Initializer</span>
|
||||
</a>
|
||||
</h1>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
Header.defaultProps = {
|
||||
children: null,
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
export default Header
|
||||
20
initializer-page/src/components/common/layout/Layout.js
Normal file
20
initializer-page/src/components/common/layout/Layout.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
import Header from './Header'
|
||||
import QuickLinks from './QuickLinks'
|
||||
|
||||
const Layout = ({ children }) => (
|
||||
<>
|
||||
<Header>
|
||||
<QuickLinks />
|
||||
</Header>
|
||||
<main>{children}</main>
|
||||
</>
|
||||
)
|
||||
|
||||
Layout.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
}
|
||||
|
||||
export default Layout
|
||||
30
initializer-page/src/components/common/layout/Logo.js
Normal file
30
initializer-page/src/components/common/layout/Logo.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
const Logo = ({ className }) => {
|
||||
return (
|
||||
<svg
|
||||
t="1583838227729"
|
||||
className={className}
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="6979" width="65" height="65">
|
||||
<path
|
||||
fill="#FF6A00"
|
||||
p-id="6980"
|
||||
d="M853.333333 852.48h-225.706666l54.613333-77.226667 164.693333-50.346666c29.866667-9.386667 49.92-37.12 49.92-68.266667V410.026667c0-31.146667-20.053333-58.88-49.493333-68.266667L682.666667 291.413333l-54.613334-77.226666H853.333333c94.293333 0 170.666667 76.373333 170.666667 170.666666v297.386667c0 93.866667-76.373333 170.24-170.666667 170.24zM341.333333 492.8h341.333334v76.8H341.333333v-76.8zM177.066667 341.333333c-29.866667 9.386667-49.92 37.12-49.493334 68.266667v247.04c0 31.146667 20.053333 58.88 49.493334 68.266667l164.693333 50.346666 54.613333 77.226667H170.666667c-94.293333 0-170.666667-76.373333-170.666667-170.666667V384.426667c0-94.293333 76.373333-170.666667 170.666667-170.666667h225.706666L341.76 290.986667 177.066667 341.333333z">
|
||||
</path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
Logo.defaultProps = {
|
||||
className: '',
|
||||
}
|
||||
|
||||
Logo.propTypes = {
|
||||
className: PropTypes.string,
|
||||
}
|
||||
|
||||
export default Logo
|
||||
122
initializer-page/src/components/common/layout/QuickLinks.js
Normal file
122
initializer-page/src/components/common/layout/QuickLinks.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import get from 'lodash.get'
|
||||
import React, {useContext, useEffect, useRef, useState} from 'react'
|
||||
import {CSSTransition, TransitionGroup} from 'react-transition-group'
|
||||
|
||||
import {AppContext} from '../../reducer/App'
|
||||
import {IconCaretDown, IconHelp} from '../icons'
|
||||
import {Switch} from '../form'
|
||||
|
||||
const QuickLinks = () => {
|
||||
const {theme, dispatch, language} = useContext(AppContext)
|
||||
const [help, setHelp] = useState(false)
|
||||
const wrapper = useRef(null)
|
||||
const toggleTheme = () => {
|
||||
const newTheme = theme === 'dark' ? 'light' : 'dark'
|
||||
dispatch({
|
||||
type: 'UPDATE',
|
||||
payload: {
|
||||
theme: newTheme,
|
||||
},
|
||||
})
|
||||
}
|
||||
const toggleLanguage = () => {
|
||||
const newLanguage = language === 'en' ? 'zh' : 'en'
|
||||
dispatch({
|
||||
type: 'UPDATE',
|
||||
payload: {
|
||||
language: newLanguage,
|
||||
},
|
||||
})
|
||||
}
|
||||
useEffect(() => {
|
||||
const clickOutside = event => {
|
||||
const children = get(wrapper, 'current')
|
||||
if (children && !children.contains(event.target)) {
|
||||
setHelp(false)
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', clickOutside)
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', clickOutside)
|
||||
}
|
||||
}, [setHelp])
|
||||
|
||||
/*
|
||||
<li>
|
||||
<span className='switch-language'>
|
||||
<Switch id='language-switch' isOn={language === 'en'} onChange={toggleLanguage} />
|
||||
{language === 'en' ? 'English' : '中文'}
|
||||
</span>
|
||||
</li>
|
||||
*/
|
||||
return (
|
||||
<ul className='quick-links'>
|
||||
<li>
|
||||
<span className='switch-mode'>
|
||||
<Switch id='theme-switch' isOn={theme === 'dark'} onChange={toggleTheme}/>
|
||||
{theme === 'dark' ? '深色' : '浅色'} 主题
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href='/'
|
||||
className='dropdown'
|
||||
tabIndex='-1'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
setHelp(!help)
|
||||
}}
|
||||
ref={wrapper}
|
||||
>
|
||||
<IconHelp/>
|
||||
帮助信息
|
||||
<IconCaretDown className='caret'/>
|
||||
</a>
|
||||
|
||||
<TransitionGroup component={null}>
|
||||
{help && (
|
||||
<CSSTransition classNames='nav-anim' timeout={500}>
|
||||
<ul className='dropdown-menu'>
|
||||
<li>
|
||||
<a
|
||||
id='ql-help-projects'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
href='https://spring.io/projects'
|
||||
tabIndex='-1'
|
||||
>
|
||||
Spring Projects
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
id='ql-help-guides'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
tabIndex='-1'
|
||||
href='https://spring.io/projects/spring-cloud/'
|
||||
>
|
||||
Spring Cloud
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
id='ql-help-guides'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
tabIndex='-1'
|
||||
href='https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md'
|
||||
>
|
||||
Spring Cloud Alibaba
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</CSSTransition>
|
||||
)}
|
||||
</TransitionGroup>
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export default QuickLinks
|
||||
3
initializer-page/src/components/common/layout/index.js
Normal file
3
initializer-page/src/components/common/layout/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as Layout } from './Layout'
|
||||
export { default as Footer } from './Footer'
|
||||
export { default as Header } from './Header'
|
||||
@@ -0,0 +1,27 @@
|
||||
import '../../../styles/sandbox.scss'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
function SandboxShell({position}) {
|
||||
return (
|
||||
<button
|
||||
className='button primary share-ghost'
|
||||
type='button'
|
||||
style={{
|
||||
top: `${position.y}px`,
|
||||
left: `${position.x}px`,
|
||||
}}
|
||||
>
|
||||
跑一下 ^_^
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
SandboxShell.propTypes = {
|
||||
position: PropTypes.shape({
|
||||
x: PropTypes.number.isRequired,
|
||||
y: PropTypes.number.isRequired,
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
export default SandboxShell
|
||||
35
initializer-page/src/components/common/share/Overlay.js
Normal file
35
initializer-page/src/components/common/share/Overlay.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import {CSSTransition, TransitionGroup} from 'react-transition-group'
|
||||
|
||||
import {noScroll} from '../../utils/NoScroll'
|
||||
|
||||
function Overlay({ open }) {
|
||||
const onEnter = () => {
|
||||
noScroll.on()
|
||||
}
|
||||
|
||||
const onEnded = () => {
|
||||
noScroll.off()
|
||||
}
|
||||
return (
|
||||
<TransitionGroup component={null}>
|
||||
{open && (
|
||||
<CSSTransition
|
||||
onEnter={onEnter}
|
||||
onExit={onEnded}
|
||||
classNames='overlay'
|
||||
timeout={500}
|
||||
>
|
||||
<div className='popup-share-overlay' />
|
||||
</CSSTransition>
|
||||
)}
|
||||
</TransitionGroup>
|
||||
)
|
||||
}
|
||||
|
||||
Overlay.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
export default Overlay
|
||||
146
initializer-page/src/components/common/share/Popover.js
Normal file
146
initializer-page/src/components/common/share/Popover.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import get from 'lodash.get'
|
||||
import React, {useEffect, useRef, useState} from 'react'
|
||||
import {CSSTransition, TransitionGroup} from 'react-transition-group'
|
||||
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
||||
|
||||
import useWindowsUtils from '../../utils/WindowsUtils'
|
||||
import {IconTimes} from '../icons'
|
||||
|
||||
function Popover({ shareUrl, shareSrc, open, onClose, position }) {
|
||||
const [button, setButton] = useState('Copy')
|
||||
const wrapper = useRef(null)
|
||||
const input = useRef(null)
|
||||
const link = useRef(null)
|
||||
const windowsUtils = useWindowsUtils()
|
||||
useEffect(() => {
|
||||
const clickOutside = event => {
|
||||
const children = get(wrapper, 'current')
|
||||
if (children && !children.contains(event.target)) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', clickOutside)
|
||||
if (get(input, 'current')) {
|
||||
get(input, 'current').focus()
|
||||
}
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', clickOutside)
|
||||
}
|
||||
}, [onClose, input])
|
||||
|
||||
const onEnter = () => {
|
||||
setButton('复制')
|
||||
}
|
||||
|
||||
const onCopy = () => {
|
||||
setButton('已复制!')
|
||||
input.current.focus()
|
||||
setTimeout(() => {
|
||||
onClose()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const urlToShare = `${windowsUtils.origin}/bootstrap.html/${shareSrc}#!${shareUrl}`
|
||||
return (
|
||||
<>
|
||||
<TransitionGroup component={null}>
|
||||
{open && (
|
||||
<CSSTransition onEnter={onEnter} classNames='popup' timeout={500}>
|
||||
<div
|
||||
className='popup-share'
|
||||
style={{
|
||||
top: `${position.y - 200}px`,
|
||||
left: `${position.x - 200}px`,
|
||||
}}
|
||||
>
|
||||
<div ref={wrapper}>
|
||||
<div className='popup-header'>
|
||||
<h1>分享你的配置</h1>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
onClose()
|
||||
}}
|
||||
className='close'
|
||||
>
|
||||
<IconTimes />
|
||||
</a>
|
||||
</div>
|
||||
<div className='popup-content'>
|
||||
{/* eslint-disable-next-line */}
|
||||
<label htmlFor='input-share'>
|
||||
请复制下面的链接来分享你的项目配置;
|
||||
</label>
|
||||
{
|
||||
shareSrc === ''
|
||||
?
|
||||
<label htmlFor='input-share'>
|
||||
Tips: 您未登录,请<a href="https://account.aliyun.com/login/login.htm?oauth_callback=https%3A%2F%2Fstart.aliyun.com%2Fbootstrap.html">登录</a>后再分享链接,否则该分享链接的点击讲无法关联在您的账户下
|
||||
</label>
|
||||
:
|
||||
<label htmlFor='input-share'>
|
||||
Tips: 您已登录,您可以使用一下专属链接来分享项目配置,该分享链接的点击将被关联在您的账户下
|
||||
</label>
|
||||
}
|
||||
<div className='control'>
|
||||
<input
|
||||
onFocus={event => {
|
||||
event.target.select()
|
||||
}}
|
||||
id='input-share'
|
||||
className={`control-input ${
|
||||
button === 'Copied!' ? 'padding-lg' : ''
|
||||
}`}
|
||||
readOnly
|
||||
value={urlToShare}
|
||||
ref={input}
|
||||
/>
|
||||
<CopyToClipboard onCopy={onCopy} text={urlToShare}>
|
||||
<a
|
||||
href='/#'
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
className='link'
|
||||
ref={link}
|
||||
>
|
||||
{button}
|
||||
</a>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CSSTransition>
|
||||
)}
|
||||
</TransitionGroup>
|
||||
{open && (
|
||||
<button
|
||||
className='button primary share-ghost'
|
||||
type='button'
|
||||
style={{
|
||||
top: `${position.y}px`,
|
||||
left: `${position.x}px`,
|
||||
}}
|
||||
>
|
||||
分享...
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Popover.propTypes = {
|
||||
shareUrl: PropTypes.string.isRequired,
|
||||
shareSrc: PropTypes.string.isRequired,
|
||||
open: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
position: PropTypes.shape({
|
||||
x: PropTypes.number.isRequired,
|
||||
y: PropTypes.number.isRequired,
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
export default Popover
|
||||
35
initializer-page/src/components/common/share/Share.js
Normal file
35
initializer-page/src/components/common/share/Share.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import '../../../styles/share.scss'
|
||||
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
import Overlay from './Overlay'
|
||||
import Popover from './Popover'
|
||||
|
||||
function Share({ shareUrl, shareSrc, open, onClose, position }) {
|
||||
return (
|
||||
<>
|
||||
<Popover
|
||||
open={open || false}
|
||||
shareUrl={shareUrl}
|
||||
shareSrc={shareSrc}
|
||||
position={position}
|
||||
onClose={onClose}
|
||||
/>
|
||||
<Overlay open={open || false} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Share.propTypes = {
|
||||
shareUrl: PropTypes.string.isRequired,
|
||||
shareSrc: PropTypes.string.isRequired,
|
||||
open: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
position: PropTypes.shape({
|
||||
x: PropTypes.number.isRequired,
|
||||
y: PropTypes.number.isRequired,
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
export default Share
|
||||
1
initializer-page/src/components/common/share/index.js
Normal file
1
initializer-page/src/components/common/share/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Share } from './Share'
|
||||
Reference in New Issue
Block a user