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:
@@ -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
|
||||
Reference in New Issue
Block a user