EditorContainer.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import React from 'react'
  21. import { uuid } from 'utils/util'
  22. import { IViewVariable } from '../types'
  23. import { Resizable, ResizeCallbackData } from 'libs/react-resizable'
  24. import { ISqlEditorProps } from './SqlEditorByAce'
  25. import { IViewVariableListProps } from './ViewVariableList'
  26. import { IVariableModalProps } from './VariableModal'
  27. import { ISqlPreviewProps } from './SqlPreview'
  28. import Styles from '../View.less'
  29. type TEditorSubComponents = 'SourceTable' | 'SqlEditor' | 'ViewVariableList' | 'VariableModal' | 'SqlPreview' | 'EditorBottom'
  30. interface IEditorContainerProps {
  31. visible: boolean
  32. variable: IViewVariable[]
  33. children?: React.ReactNode
  34. onVariableChange: (variable: IViewVariable[]) => void
  35. }
  36. interface IEditorContainerStates {
  37. editorHeight: number
  38. siderWidth: number
  39. previewHeight: number
  40. variableModalVisible: boolean
  41. editingVariable: IViewVariable
  42. }
  43. export class EditorContainer extends React.Component<IEditorContainerProps, IEditorContainerStates> {
  44. private editor = React.createRef<HTMLDivElement>()
  45. public static SiderMinWidth = 250
  46. public static EditorMinHeight = 100
  47. public static DefaultPreviewHeight = 300
  48. public state: Readonly<IEditorContainerStates> = {
  49. editorHeight: 0,
  50. siderWidth: EditorContainer.SiderMinWidth,
  51. previewHeight: EditorContainer.DefaultPreviewHeight,
  52. variableModalVisible: false,
  53. editingVariable: null
  54. }
  55. public componentDidMount () {
  56. window.addEventListener('resize', this.setEditorHeight, false)
  57. // @FIX for this init height, 64px is the height of the hidden navigator in Main.tsx
  58. const editorHeight = this.editor.current.clientHeight
  59. this.setState({
  60. editorHeight
  61. })
  62. }
  63. public componentWillUnmount () {
  64. window.removeEventListener('resize', this.setEditorHeight, false)
  65. }
  66. public setEditorHeight = () => {
  67. const editorHeight = this.editor.current.clientHeight
  68. const { previewHeight, editorHeight: oldEditorHeight } = this.state
  69. const newPreviewHeight = Math.min(Math.floor(previewHeight * (editorHeight / oldEditorHeight)), editorHeight)
  70. this.setState({
  71. editorHeight,
  72. previewHeight: newPreviewHeight
  73. })
  74. }
  75. private siderResize = (_: any, { size }: ResizeCallbackData) => {
  76. const { width } = size
  77. this.setState({ siderWidth: width })
  78. }
  79. private previewResize = (_: any, { size }: ResizeCallbackData) => {
  80. const { height } = size
  81. this.setState(({ editorHeight }) => ({ previewHeight: editorHeight - height }))
  82. }
  83. private addVariable = () => {
  84. this.setState({
  85. editingVariable: null,
  86. variableModalVisible: true
  87. })
  88. }
  89. private saveVariable = (updatedVariable: IViewVariable) => {
  90. const { variable, onVariableChange } = this.props
  91. const updatedViewVariables = [...variable]
  92. if (!updatedVariable.key) {
  93. updatedVariable.key = uuid(5)
  94. updatedViewVariables.push(updatedVariable)
  95. } else {
  96. const idx = variable.findIndex((v) => v.key === updatedVariable.key)
  97. updatedViewVariables[idx] = updatedVariable
  98. }
  99. onVariableChange(updatedViewVariables)
  100. this.setState({
  101. variableModalVisible: false
  102. })
  103. }
  104. private deleteVariable = (key: string) => {
  105. const { variable, onVariableChange } = this.props
  106. const updatedViewVariables = variable.filter((v) => v.key !== key)
  107. onVariableChange(updatedViewVariables)
  108. }
  109. private editVariable = (variable: IViewVariable) => {
  110. this.setState({
  111. editingVariable: variable,
  112. variableModalVisible: true
  113. })
  114. }
  115. private variableNameValidate = (key: string, name: string, callback: (msg?: string) => void) => {
  116. const { variable } = this.props
  117. const existed = variable.findIndex((v) => ((!key || v.key !== key) && v.name === name)) >= 0
  118. if (existed) {
  119. callback('名称不能重复')
  120. return
  121. }
  122. callback()
  123. }
  124. private closeVariableModal = () => {
  125. this.setState({ variableModalVisible: false })
  126. }
  127. private getChildren = (props: IEditorContainerProps, state: IEditorContainerStates) => {
  128. const obj = {}
  129. React.Children.forEach(props.children, (child: React.ReactElement<any>) => {
  130. const name = child.key as TEditorSubComponents
  131. if (name === 'ViewVariableList') {
  132. obj[name] = React.cloneElement<IViewVariableListProps>(child, {
  133. className: Styles.viewVariable,
  134. onAdd: this.addVariable,
  135. onDelete: this.deleteVariable,
  136. onEdit: this.editVariable
  137. })
  138. } else if (name === 'VariableModal') {
  139. const { variableModalVisible, editingVariable } = this.state
  140. obj[name] = React.cloneElement<IVariableModalProps>(child, {
  141. visible: variableModalVisible,
  142. variable: editingVariable,
  143. nameValidator: this.variableNameValidate,
  144. onCancel: this.closeVariableModal,
  145. onSave: this.saveVariable
  146. })
  147. } else if (name === 'SqlPreview') {
  148. const { previewHeight } = state
  149. obj[name] = React.cloneElement<ISqlPreviewProps>(child, { height: previewHeight })
  150. } else if (name === 'SqlEditor') {
  151. const { previewHeight } = state
  152. obj[name] = React.cloneElement<ISqlEditorProps>(child, { sizeChanged: previewHeight })
  153. } else {
  154. obj[name] = child
  155. }
  156. })
  157. return obj as Record<TEditorSubComponents, React.ReactElement<any>>
  158. }
  159. public render () {
  160. const { visible } = this.props
  161. const {
  162. editorHeight, siderWidth, previewHeight } = this.state
  163. const style = visible ? {} : { display: 'none' }
  164. const { SourceTable, SqlEditor, SqlPreview, EditorBottom, ViewVariableList, VariableModal } = this.getChildren(this.props, this.state)
  165. return (
  166. <>
  167. <div className={Styles.containerVertical} style={style}>
  168. <div className={Styles.sider} style={{ width: siderWidth }}>
  169. <Resizable
  170. axis="x"
  171. width={siderWidth}
  172. height={0}
  173. minConstraints={[EditorContainer.SiderMinWidth, 0]}
  174. maxConstraints={[EditorContainer.SiderMinWidth * 2, 0]}
  175. onResize={this.siderResize}
  176. >
  177. <div>{SourceTable}</div>
  178. </Resizable>
  179. </div>
  180. <div className={Styles.containerHorizontal}>
  181. <div className={Styles.containerHorizontal} ref={this.editor}>
  182. <div className={Styles.right} style={{ height: editorHeight - previewHeight }}>
  183. <Resizable
  184. axis="y"
  185. width={0}
  186. height={editorHeight - previewHeight}
  187. minConstraints={[0, EditorContainer.EditorMinHeight]}
  188. maxConstraints={[0, editorHeight]}
  189. onResize={this.previewResize}
  190. >
  191. <div className={Styles.containerVertical}>
  192. {SqlEditor}
  193. {ViewVariableList}
  194. </div>
  195. </Resizable>
  196. </div>
  197. <div className={Styles.preview} style={{ height: previewHeight }}>
  198. {SqlPreview}
  199. </div>
  200. </div>
  201. {EditorBottom}
  202. </div>
  203. </div>
  204. {VariableModal}
  205. </>
  206. )
  207. }
  208. }
  209. export default EditorContainer