diff --git a/src/components/editor/InterfaceEditor.tsx b/src/components/editor/InterfaceEditor.tsx index 83cdf37..80d3917 100644 --- a/src/components/editor/InterfaceEditor.tsx +++ b/src/components/editor/InterfaceEditor.tsx @@ -124,6 +124,7 @@ class InterfaceEditor extends Component< /> Mock.mock({ 'scope|1': ['request', 'response'], name: '@WORD(6)', - 'type|1': TYPES, + 'type|1': ['String', 'Number', 'Boolean'], 'value|1': ['@INT', '@FLOAT', '@TITLE', '@NAME'], description: '@CSENTENCE', parentId: -1, diff --git a/src/components/editor/PropertyList.tsx b/src/components/editor/PropertyList.tsx index 3b3b24f..7a6e797 100644 --- a/src/components/editor/PropertyList.tsx +++ b/src/components/editor/PropertyList.tsx @@ -8,10 +8,38 @@ import { GoPlus, GoTrashcan, GoQuestion } from 'react-icons/go' import { rptFromStr2Num } from './InterfaceSummary' import './PropertyList.css' import { ButtonGroup, Button, Checkbox } from '@material-ui/core' +import classNames from 'classnames' +import _ from 'lodash' +import Mock from 'mockjs' import JSON5 from 'json5' +import { elementInViewport } from 'utils/ElementInViewport' + +const mockProperty = process.env.NODE_ENV === 'development' + ? () => Mock.mock({ + 'scope|1': ['request', 'response'], + name: '@WORD(6)', + 'type|1': ['String', 'Number', 'Boolean'], + 'value|1': ['@INT', '@FLOAT', '@TITLE', '@NAME'], + description: '@CSENTENCE', + parentId: -1, + interfaceId: '@NATURAL', + moduleId: '@NATURAL', + repositoryId: '@NATURAL', + }) + : () => ({ + scope: 'response', + name: '', + type: 'String', + value: '', + description: '', + parentId: -1, + interfaceId: undefined, + moduleId: undefined, + repositoryId: undefined, + }) export const RequestPropertyListPreviewer = (props: any) => ( - + ) export const ResponsePropertyListPreviewer = (props: any) => ( @@ -94,15 +122,40 @@ const getFormattedValue = (itf: any) => { } class SortableTreeTableRow extends Component { + focusNameInput: HTMLInputElement | undefined = undefined + state = { + property: { children: [] }, + childrenAdded: false, + } + static getDerivedStateFromProps(nextProps: any, prevState: any) { + return { + property: nextProps.property, + childrenAdded: nextProps.property.children.length > prevState.property.children.length, + } + } + componentDidMount() { + this.focusInput() + } + componentDidUpdate() { + this.focusInput() + } + focusInput() { + if (this.focusNameInput && this.state.childrenAdded) { + this.focusNameInput.focus() + if (!elementInViewport(this.focusNameInput)) { + this.focusNameInput.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' }) + } + } + } render() { - const { property, editable } = this.props - const { handleClickCreateChildPropertyButton, handleDeleteMemoryProperty, handleChangePropertyField, handleSortProperties } = this.props + const { property, editable, handleClickCreateChildPropertyButton, highlightId, + handleDeleteMemoryProperty, handleChangePropertyField, handleSortProperties } = this.props return (
{property.children.sort((a: any, b: any) => a.priority - b.priority).map((item: any) =>
-
+
{editable &&
{(item.type === 'Object' || item.type === 'Array') @@ -125,6 +178,11 @@ class SortableTreeTableRow extends Component {
: null} : { + if (item.id === highlightId) { + this.focusNameInput = input + } + }} value={item.name} onChange={e => handleChangePropertyField(item.id, 'name', e.target.value)} className="form-control editable" @@ -231,18 +289,22 @@ class PropertyList extends Component { label: PropTypes.string.isRequired, scope: PropTypes.string.isRequired, properties: PropTypes.array, + auth: PropTypes.object.isRequired, repository: PropTypes.object.isRequired, mod: PropTypes.object.isRequired, itf: PropTypes.object.isRequired, editable: PropTypes.bool.isRequired, - /** optional */ bodyOption: PropTypes.string, requestParamsType: PropTypes.string, } + static contextTypes = { + handleAddMemoryProperty: PropTypes.func.isRequired, + } constructor(props: any) { super(props) this.state = { + highlightId: undefined, createProperty: false, createChildProperty: false, previewer: props.scope === 'response', @@ -279,6 +341,8 @@ class PropertyList extends Component { { ) } handleClickCreatePropertyButton = () => { - this.setState({ createProperty: true }) + this.handleClickCreateChildPropertyButton() } - handleClickCreateChildPropertyButton = (item: any) => { - this.setState({ createChildProperty: item }) + // handlefocused = () => { + // this.setState({ highlightId: undefined }) + // } + handleClickCreateChildPropertyButton = (parent: any = { id: -1 }) => { + const { handleAddMemoryProperty } = this.context + const { auth, scope, repository = {}, mod = {}, itf = {} } = this.props + const childId = _.uniqueId('memory-') + const child = { + ...mockProperty(), + id: childId, + creatorId: auth.id, + repositoryId: repository.id, + moduleId: mod.id, + interfaceId: itf.id, + scope, + parentId: parent.id, + } + this.setState({ + highlightId: childId, + }) + handleAddMemoryProperty(child, () => { + /** empty */ + }) } handleClickImporterButton = () => { this.setState({ importer: true }) diff --git a/src/components/editor/RepositoryEditor.sass b/src/components/editor/RepositoryEditor.sass index 9c6dbca..66b2e30 100644 --- a/src/components/editor/RepositoryEditor.sass +++ b/src/components/editor/RepositoryEditor.sass @@ -317,11 +317,22 @@ text-align: center color: #999 +@keyframes hightlight + 0% + background-color: unset; + 50% + background-color: #fff3cd + 100% + background-color: unset; + .SortableTreeTable .SortableTreeTableHeader, .SortableTreeTableRow .flex-row display: flex + &.highlight + .thtd + animation: hightlight 1.5s 1 .thtd border: 1px solid #eceeef flex-grow: 1 @@ -340,7 +351,6 @@ margin-bottom: -1px .th, .td &.operations - background: #fff width: 4.5rem min-width: 4.5rem &.name @@ -413,7 +423,9 @@ &.payload.name @for $i from 0 through 10 &.depth-#{$i} - padding-left: $i * 1rem + padding-left: $i * 1.3rem + &:after + width: $i * 1.3rem &.payload.value max-height: unset overflow-wrap: break-word diff --git a/src/components/editor/RepositoryEditor.tsx b/src/components/editor/RepositoryEditor.tsx index a1c28dc..d2af36a 100644 --- a/src/components/editor/RepositoryEditor.tsx +++ b/src/components/editor/RepositoryEditor.tsx @@ -63,10 +63,10 @@ class RepositoryEditor extends Component { } componentDidMount() { - const id = +this.props.location.params.id - if (!this.props.repository.data || this.props.repository.data.id !== id) { - this.props.onFetchRepository({ id }) - } + // const id = +this.props.location.params.id + // if (!this.props.repository.data || this.props.repository.data.id !== id) { + // this.props.onFetchRepository({ id }) + // } } render() { const { location: { params }, auth } = this.props diff --git a/src/relatives/RepositoryRelative.tsx b/src/relatives/RepositoryRelative.tsx index c30abec..d3560b1 100644 --- a/src/relatives/RepositoryRelative.tsx +++ b/src/relatives/RepositoryRelative.tsx @@ -172,42 +172,44 @@ export default { ), }, } - case 'INTERFACE_LIST_SORT_SUCCEEDED': - modules = state.data.modules - const iftIds = action.ids - const itfIdsMap: any = {} - iftIds.forEach((id: number, index: number) => { - itfIdsMap[id] = index - }) - const moduleId = action.moduleId - return { - ...state, - data: { - ...state.data, - modules: modules.map((mod: any) => - mod.id === moduleId - ? { - ...mod, - interfaces: [...mod.interfaces].sort((a: any, b: any) => itfIdsMap[a.id] - itfIdsMap[b.id]), - } - : mod - ), - }, - } - case 'MODULE_LIST_SORT_SUCCEEDED': - modules = state.data.modules - const moduleIds = action.ids - const moduleIdsMap: any = {} - moduleIds.forEach((id: number, index: number) => { - moduleIdsMap[id] = index - }) - return { - ...state, - data: { - ...state.data, - modules: [...modules].sort((a: any, b: any) => moduleIdsMap[a.id] - moduleIdsMap[b.id]), - }, - } + case 'INTERFACE_LIST_SORT_SUCCEEDED': { + const modules = state.data.modules + const iftIds = action.ids + const itfIdsMap: any = {} + iftIds.forEach((id: number, index: number) => { + itfIdsMap[id] = index + }) + const moduleId = action.moduleId + return { + ...state, + data: { + ...state.data, + modules: modules.map((mod: any) => + mod.id === moduleId + ? { + ...mod, + interfaces: [...mod.interfaces].sort((a: any, b: any) => itfIdsMap[a.id] - itfIdsMap[b.id]), + } + : mod + ), + }, + } + } + case 'MODULE_LIST_SORT_SUCCEEDED': { + const modules = state.data.modules + const moduleIds = action.ids + const moduleIdsMap: any = {} + moduleIds.forEach((id: number, index: number) => { + moduleIdsMap[id] = index + }) + return { + ...state, + data: { + ...state.data, + modules: [...modules].sort((a: any, b: any) => moduleIdsMap[a.id] - moduleIdsMap[b.id]), + }, + } + } default: return state } diff --git a/src/relatives/effects/repository.ts b/src/relatives/effects/repository.ts index 586a192..a121ce0 100644 --- a/src/relatives/effects/repository.ts +++ b/src/relatives/effects/repository.ts @@ -46,8 +46,9 @@ export function* updateRepository(action: any) { acceptedKeys.forEach(x => { params[x] = r[x] }) - const repository = yield call(RepositoryService.updateRepository, params) - yield put(RepositoryAction.updateRepositorySucceeded(repository)) + yield call(RepositoryService.updateRepository, params) + yield put(RepositoryAction.updateRepositorySucceeded(params)) + yield put(RepositoryAction.fetchRepository({id: params.id})) if (action.onResolved) { action.onResolved() } } catch (e) { yield put(RepositoryAction.updateRepositoryFailed(e.message)) diff --git a/src/utils/ElementInViewport.ts b/src/utils/ElementInViewport.ts new file mode 100644 index 0000000..6d611fd --- /dev/null +++ b/src/utils/ElementInViewport.ts @@ -0,0 +1,19 @@ +export function elementInViewport(el: HTMLElement) { + let top = el.offsetTop + let left = el.offsetLeft + const width = el.offsetWidth + const height = el.offsetHeight + + while (el.offsetParent) { + el = el.offsetParent as HTMLElement + top += el.offsetTop + left += el.offsetLeft + } + + return ( + top >= window.pageYOffset && + left >= window.pageXOffset && + (top + height) <= (window.pageYOffset + window.innerHeight) && + (left + width) <= (window.pageXOffset + window.innerWidth) + ) +}