Added Swagger
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
/* global ace */
|
||||
ace.define("ace/snippets/yaml",
|
||||
["require","exports","module"], function(e,t,n){ // eslint-disable-line no-unused-vars
|
||||
t.snippetText=undefined
|
||||
t.scope="yaml"
|
||||
})
|
||||
@@ -0,0 +1,65 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export default class EditorContainer extends React.Component {
|
||||
|
||||
// This is already debounced by editor.jsx
|
||||
onChange = (value) => {
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
let { specSelectors, getComponent, errSelectors, fn, editorSelectors, configsSelectors } = this.props
|
||||
|
||||
let Editor = getComponent("Editor")
|
||||
|
||||
let wrapperClasses = ["editor-wrapper"]
|
||||
const readOnly = !!configsSelectors.get("readOnly")
|
||||
|
||||
if(readOnly) {
|
||||
wrapperClasses.push("read-only")
|
||||
}
|
||||
|
||||
let propsForEditor = this.props
|
||||
|
||||
const editorOptions = {
|
||||
enableLiveAutocompletion: configsSelectors.get("editorLiveAutocomplete"),
|
||||
readOnly: readOnly,
|
||||
highlightActiveLine: !readOnly,
|
||||
highlightGutterLine: !readOnly,
|
||||
}
|
||||
|
||||
return (
|
||||
<div id='editor-wrapper' className={wrapperClasses.join(" ")}>
|
||||
{ readOnly ? <h2 className="editor-readonly-watermark">Read Only</h2> : null }
|
||||
<Editor
|
||||
{...propsForEditor}
|
||||
value={specSelectors.specStr()}
|
||||
origin={specSelectors.specOrigin()}
|
||||
editorOptions={editorOptions}
|
||||
specObject={specSelectors.specJson().toJS()}
|
||||
errors={errSelectors.allErrors()}
|
||||
onChange={this.onChange}
|
||||
goToLine={editorSelectors.gotoLine()}
|
||||
AST={fn.AST}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EditorContainer.defaultProps = {
|
||||
onChange: Function.prototype
|
||||
}
|
||||
|
||||
EditorContainer.propTypes = {
|
||||
specActions: PropTypes.object.isRequired,
|
||||
configsSelectors: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func,
|
||||
fn: PropTypes.object,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
errSelectors: PropTypes.object.isRequired,
|
||||
editorSelectors: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import AceEditor from "react-ace"
|
||||
import editorPluginsHook from "../editor-plugins/hook"
|
||||
import { placeMarkerDecorations } from "../editor-helpers/marker-placer"
|
||||
import Im, { fromJS } from "immutable"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
import win from "src/window"
|
||||
|
||||
import isUndefined from "lodash/isUndefined"
|
||||
import omit from "lodash/omit"
|
||||
import isEqual from "lodash/isEqual"
|
||||
import debounce from "lodash/debounce"
|
||||
|
||||
import ace from "brace"
|
||||
import "brace/mode/yaml"
|
||||
import "brace/theme/tomorrow_night_eighties"
|
||||
import "brace/ext/language_tools"
|
||||
import "brace/ext/searchbox"
|
||||
import "./brace-snippets-yaml"
|
||||
|
||||
const NOOP = Function.prototype // Apparently the best way to no-op
|
||||
|
||||
export default function makeEditor({ editorPluginsToRun }) {
|
||||
|
||||
class Editor extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
|
||||
this.editor = null
|
||||
|
||||
this.debouncedOnChange = props.debounce > 0
|
||||
? debounce(props.onChange, props.debounce)
|
||||
: props.onChange
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
specId: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
editorOptions: PropTypes.object,
|
||||
origin: PropTypes.string,
|
||||
debounce: PropTypes.number,
|
||||
|
||||
onChange: PropTypes.func,
|
||||
onMarkerLineUpdate: PropTypes.func,
|
||||
|
||||
markers: PropTypes.object,
|
||||
goToLine: PropTypes.object,
|
||||
specObject: PropTypes.object.isRequired,
|
||||
|
||||
editorActions: PropTypes.object,
|
||||
|
||||
AST: PropTypes.object.isRequired,
|
||||
|
||||
errors: ImPropTypes.list,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
value: "",
|
||||
specId: "--unknown--",
|
||||
origin: "not-editor",
|
||||
onChange: NOOP,
|
||||
onMarkerLineUpdate: NOOP,
|
||||
markers: {},
|
||||
goToLine: {},
|
||||
errors: fromJS([]),
|
||||
editorActions: {onLoad(){}},
|
||||
editorOptions: {},
|
||||
debounce: 800 // 0.5 imperial seconds™
|
||||
|
||||
}
|
||||
|
||||
checkForSilentOnChange = (value) => {
|
||||
if(!this.silent) {
|
||||
this.debouncedOnChange(value)
|
||||
}
|
||||
}
|
||||
|
||||
onLoad = (editor) => {
|
||||
|
||||
const { props } = this
|
||||
const { AST, specObject } = props
|
||||
|
||||
const langTools = ace.acequire("ace/ext/language_tools")
|
||||
const session = editor.getSession()
|
||||
|
||||
this.editor = editor
|
||||
|
||||
// fixes a warning, see https://github.com/ajaxorg/ace/issues/2499
|
||||
editor.$blockScrolling = Infinity
|
||||
|
||||
|
||||
session.setUseWrapMode(true)
|
||||
session.on("changeScrollLeft", xPos => { // eslint-disable-line no-unused-vars
|
||||
session.setScrollLeft(0)
|
||||
})
|
||||
|
||||
// TODO Remove this in favour of editorActions.onLoad
|
||||
editorPluginsHook(editor, props, editorPluginsToRun || [], {
|
||||
langTools, AST, specObject
|
||||
})
|
||||
|
||||
editor.setHighlightActiveLine(false)
|
||||
editor.setHighlightActiveLine(true)
|
||||
this.syncOptionsFromState(props.editorOptions)
|
||||
if(props.editorActions && props.editorActions.onLoad)
|
||||
props.editorActions.onLoad({...props, langTools, editor})
|
||||
|
||||
this.updateMarkerAnnotations(this.props)
|
||||
}
|
||||
|
||||
onResize = () => {
|
||||
const { editor } = this
|
||||
if(editor) {
|
||||
let session = editor.getSession()
|
||||
editor.resize()
|
||||
let wrapLimit = session.getWrapLimit()
|
||||
editor.setPrintMarginColumn(wrapLimit)
|
||||
}
|
||||
}
|
||||
|
||||
onClick = () => {
|
||||
// onClick is deferred by 40ms, to give element resizes time to settle.
|
||||
setTimeout(() => {
|
||||
if(this.getWidth() !== this.width) {
|
||||
this.onResize()
|
||||
this.width = this.getWidth()
|
||||
}
|
||||
}, 40)
|
||||
}
|
||||
|
||||
getWidth = () => {
|
||||
let el = win.document.getElementById("editor-wrapper")
|
||||
return el ? el.getBoundingClientRect().width : null
|
||||
}
|
||||
|
||||
updateErrorAnnotations = (nextProps) => {
|
||||
if(this.editor && nextProps.errors) {
|
||||
let editorAnnotations = nextProps.errors.toJS().map(err => {
|
||||
// Create annotation objects that ACE can use
|
||||
return {
|
||||
row: err.line - 1,
|
||||
column: 0,
|
||||
type: err.level,
|
||||
text: err.message
|
||||
}
|
||||
})
|
||||
|
||||
this.editor.getSession().setAnnotations(editorAnnotations)
|
||||
}
|
||||
}
|
||||
|
||||
updateMarkerAnnotations = (props) => {
|
||||
const { editor } = this
|
||||
|
||||
const markers = Im.Map.isMap(props.markers) ? props.markers.toJS() : {}
|
||||
this._removeMarkers = placeMarkerDecorations({
|
||||
editor,
|
||||
markers,
|
||||
onMarkerLineUpdate: props.onMarkerLineUpdate,
|
||||
})
|
||||
}
|
||||
|
||||
removeMarkers = () => {
|
||||
if(this._removeMarkers) {
|
||||
this._removeMarkers()
|
||||
this._removeMarkers = null
|
||||
}
|
||||
}
|
||||
|
||||
shouldUpdateYaml = (props) => {
|
||||
// No editor instance
|
||||
if(!this.editor)
|
||||
return false
|
||||
|
||||
// Origin is editor
|
||||
if(props.origin === "editor")
|
||||
return false
|
||||
|
||||
// Redundant
|
||||
if(this.editor.getValue() === props.value)
|
||||
return false
|
||||
|
||||
// Value and origin are same, no update.
|
||||
if(this.props.value === props.value
|
||||
&& this.props.origin === props.origin)
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
shouldUpdateMarkers = (props) => {
|
||||
const { markers } = props
|
||||
if(Im.Map.isMap(markers)) {
|
||||
return !Im.is(markers, this.props.markers) // Different from previous?
|
||||
}
|
||||
return true // Not going to do a deep compare of object-like markers
|
||||
}
|
||||
|
||||
updateYamlAndMarkers = (props) => {
|
||||
// If we update the yaml, we need to "lift" the yaml first
|
||||
if(this.shouldUpdateYaml(props)) {
|
||||
this.removeMarkers()
|
||||
this.updateYaml(props)
|
||||
this.updateMarkerAnnotations(props)
|
||||
|
||||
} else if (this.shouldUpdateMarkers(props)) {
|
||||
this.removeMarkers()
|
||||
this.updateMarkerAnnotations(props)
|
||||
}
|
||||
}
|
||||
|
||||
updateYaml = (props) => {
|
||||
if (props.origin === "insert") {
|
||||
// Don't clobber the undo stack in this case.
|
||||
this.editor.session.doc.setValue(props.value)
|
||||
this.editor.selection.clearSelection()
|
||||
} else {
|
||||
// session.setValue does not trigger onChange, nor add to undo stack.
|
||||
// Neither of which we want here.
|
||||
this.editor.session.setValue(props.value)
|
||||
}
|
||||
}
|
||||
|
||||
syncOptionsFromState = (editorOptions={}) => {
|
||||
const { editor } = this
|
||||
if(!editor) {
|
||||
return
|
||||
}
|
||||
|
||||
const setOptions = omit(editorOptions, ["readOnly"])
|
||||
editor.setOptions(setOptions)
|
||||
|
||||
|
||||
const readOnly = isUndefined(editorOptions.readOnly)
|
||||
? false
|
||||
: editorOptions.readOnly // If its undefined, default to false.
|
||||
editor.setReadOnly(readOnly)
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
// add user agent info to document
|
||||
// allows our custom Editor styling for IE10 to take effect
|
||||
var doc = win.document.documentElement
|
||||
doc.setAttribute("data-useragent", win.navigator.userAgent)
|
||||
this.syncOptionsFromState(this.props.editorOptions)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// eslint-disable-next-line react/no-did-mount-set-state
|
||||
|
||||
this.width = this.getWidth()
|
||||
win.document.addEventListener("click", this.onClick)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
win.document.removeEventListener("click", this.onClick)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
let hasChanged = (k) => !isEqual(nextProps[k], this.props[k])
|
||||
const editor = this.editor
|
||||
|
||||
// Change the debounce value/func
|
||||
if(this.props.debounce !== nextProps.debounce) {
|
||||
if(this.debouncedOnChange.flush)
|
||||
this.debouncedOnChange.flush()
|
||||
|
||||
this.debouncedOnChange = nextProps.debounce > 0
|
||||
? debounce(nextProps.onChange, nextProps.debounce)
|
||||
: nextProps.onChange
|
||||
}
|
||||
|
||||
this.updateYamlAndMarkers(nextProps)
|
||||
this.updateErrorAnnotations(nextProps)
|
||||
|
||||
if(hasChanged("editorOptions")) {
|
||||
this.syncOptionsFromState(nextProps.editorOptions)
|
||||
}
|
||||
|
||||
if(editor && nextProps.goToLine && nextProps.goToLine.line && hasChanged("goToLine")) {
|
||||
editor.gotoLine(nextProps.goToLine.line)
|
||||
nextProps.editorActions.jumpToLine(null)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false // Never update, see: componentWillRecieveProps and this.updateYaml for where we update things.
|
||||
}
|
||||
|
||||
render() {
|
||||
// NOTE: we're manually managing the value lifecycle, outside of react render
|
||||
// This will only render once.
|
||||
return (
|
||||
<AceEditor
|
||||
mode="yaml"
|
||||
theme="tomorrow_night_eighties"
|
||||
value={this.props.value /* This will only load once, thereafter it'll be via updateYaml */}
|
||||
onLoad={this.onLoad}
|
||||
onChange={this.checkForSilentOnChange}
|
||||
name="ace-editor"
|
||||
width="100%"
|
||||
height="100%"
|
||||
tabSize={2}
|
||||
fontSize={14}
|
||||
useSoftTabs="true"
|
||||
wrapEnabled={true}
|
||||
editorProps={{
|
||||
"display_indent_guides": true,
|
||||
folding: "markbeginandend"
|
||||
}}
|
||||
setOptions={{
|
||||
cursorStyle: "smooth",
|
||||
wrapBehavioursEnabled: true
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Editor
|
||||
}
|
||||
Reference in New Issue
Block a user