Fixed some bugs with the ThangComponentsEditView, added AddThangComponentsModal.

This commit is contained in:
Scott Erickson 2014-08-11 13:24:08 -07:00
parent 023fd40767
commit b3572dfad9
23 changed files with 431 additions and 77 deletions

View file

@ -263,6 +263,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
this.sendResponses = function(responseMap) {
var urls = Object.keys(responseMap);
var success = true;
for(var i in urls) {
var url = urls[i];
var responseBody = responseMap[url];
@ -282,9 +283,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
urls = [];
for(var k in allRequests) urls.push(allRequests[k].url);
console.error('could not find response for', url, 'in', urls, allRequests);
success = false;
continue;
}
}
return success;
}
}

View file

@ -116,6 +116,10 @@ a
.background-wrapper.plain
background: white
.plain
h1, h2, h3, h4, h5, h6
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important
.modal-content
@include box-shadow(none)

View file

@ -0,0 +1,24 @@
#add-thang-components-modal
.list-group
max-height: 500px
overflow: scroll
.item-title
cursor: pointer
.glyphicon-chevron-down
display: none
&.collapsed
.glyphicon-chevron-down
display: inline
.glyphicon-chevron-up
display: none
.list-group-item
padding: 5px
.checkbox, .item-title
overflow: hidden
text-overflow: ellipsis
white-space: nowrap

View file

@ -0,0 +1,6 @@
.thang-component-config-view
.panel-body
padding: 0
.treema-root
border: 0
padding: 0 5px

View file

@ -1,6 +1,6 @@
#thang-components-edit-view
position: absolute
top: 40px
top: 0
bottom: 0
left: 0
right: 0
@ -17,7 +17,7 @@
.treema-dependent > .treema-row
background-color: #FFC671
#extant-components-column
#thang-components-column
left: 0
width: 300px

View file

@ -1 +0,0 @@
//.thang-component-config-view

View file

@ -1,4 +1,4 @@
#editor-level-thang-edit
#level-thang-edit-view
color: black
.well
@ -13,3 +13,6 @@
#all-thangs-link
float: left
cursor: pointer
#thang-components-edit-view
top: 50px

View file

@ -0,0 +1,22 @@
extends /templates/modal/modal_base
block modal-header-content
h1 Add Components
block modal-body-content
form
ul.list-group
for system in systems
li.list-group-item
div.item-title(data-toggle="collapse", data-target="##{system}").collapsed
span.glyphicon.glyphicon-chevron-down.text-muted
span.glyphicon.glyphicon-chevron-up.text-muted
a.spl= system
span.spl.text-muted= nameLists[system]
.collapse-panel.collapse(id=system)
for component in components[system]
.checkbox
label
input(type='checkbox', value=component.id)
span.spr #{component.get('name')}:
span.text-muted= component.get('description')

View file

@ -0,0 +1,7 @@
.panel.panel-default
.panel-heading
em #{component.system}.
strong.panel-title.spr= component.name
span#description.text-muted= component.description
.panel-body
.treema

View file

@ -1,6 +1,6 @@
#extant-components-column.column
#thang-components-column.column
h3 Components
p Click then press delete to remove.
button.btn#add-components-button Add Components
.treema
.column#thang-components-config-column

View file

@ -1,21 +0,0 @@
table.table
tr
th(colspan=3)
span(data-i18n="general.results")
| Results
span
|: #{documents.length}
tr
th(data-i18n="general.name") Name
th(data-i18n="general.description") Description
th(data-i18n="general.version") Version
for data in documents
- data = data.attributes;
tr
td
a(href="/editor/component/#{data.slug || data._id}")
| #{data.name}
td.body-row #{data.description}
td #{data.version.major}.#{data.version.minor}

View file

@ -1,7 +0,0 @@
.panel.panel-default
.panel-heading
strong.panel-title= component.name
em.spl.spr (#{component.system})
span#description= component.description
.panel-body
.treema

View file

@ -36,4 +36,4 @@ button.navbar-toggle.toggle.btn-primary#thangs-palette-toggle(type="button", dat
#canvas-top-gradient.gradient
.add-thangs-palette.thangs-column#add-thangs-column
#editor-level-thang-edit.secret
#level-thang-edit-view.secret

View file

@ -1,4 +1,5 @@
CocoView = require 'views/kinds/CocoView'
ModalView = require 'views/kinds/ModalView'
template = require 'templates/demo'
requireUtils = require 'lib/requireUtils'
@ -81,7 +82,10 @@ module.exports = DemoView = class DemoView extends CocoView
jasmine.Ajax.install()
view = demoFunc()
return unless view
@$el.find('#demo-area').empty().append(view.$el)
if view instanceof ModalView
@openModalView(view)
else
@$el.find('#demo-area').empty().append(view.$el)
view.afterInsert()
# TODO, maybe handle root views differently than modal views differently than everything else?

View file

@ -0,0 +1,40 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/editor/component/add-thang-components-modal'
CocoCollection = require 'collections/CocoCollection'
LevelComponent = require 'models/LevelComponent'
module.exports = class UnnamedView extends ModalView
id: 'add-thang-components-modal'
template: template
plain: true
modalWidthPercent: 80
events:
'click .footer button': 'onDonePressed'
initialize: (options) ->
super()
@skipOriginals = options.skipOriginals or []
@components = new CocoCollection([], model: LevelComponent)
@components.url = "/db/level.component?term=&project=name,system,original,version,description"
@supermodel.loadCollection(@components, 'components')
getRenderData: ->
c = super()
c.components = (comp for comp in @components.models when not (comp.get('original') in @skipOriginals))
c.components = _.groupBy(c.components, (comp) -> comp.get('system'))
c.nameLists = {}
for system, componentList of c.components
c.components[system] = _.sortBy(componentList, (comp) -> comp.get('name'))
c.nameLists[system] = (comp.get('name') for comp in c.components[system]).join(', ')
c.systems = _.keys(c.components)
c.systems.sort()
c
getSelectedComponents: ->
selected = @$el.find('input[type="checkbox"]:checked')
vals = ($(el).val() for el in selected)
components = (c for c in @components.models when c.id in vals)
return components
# sparseComponents = ({original: c.get('original'), majorVersion: c.get('version').major} for c in components)
# return sparseComponents

View file

@ -1,5 +1,5 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/editor/components/thang-component-config-view'
template = require 'templates/editor/component/thang-component-config-view'
Level = require 'models/Level'
LevelComponent = require 'models/LevelComponent'

View file

@ -1,15 +1,19 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/editor/components/thang-components-edit-view'
template = require 'templates/editor/component/thang-components-edit-view'
Level = require 'models/Level'
LevelComponent = require 'models/LevelComponent'
LevelSystem = require 'models/LevelSystem'
ComponentsCollection = require 'collections/ComponentsCollection'
ThangComponentConfigView = require './ThangComponentConfigView'
AddThangComponentsModal = require './AddThangComponentsModal'
module.exports = class ThangComponentsEditView extends CocoView
id: 'thang-components-edit-view'
template: template
events:
'click #add-components-button': 'onAddComponentsButtonClicked'
constructor: (options) ->
super options
@ -18,29 +22,28 @@ module.exports = class ThangComponentsEditView extends CocoView
@lastComponentLength = @components.length
@world = options.world
@level = options.level
@callback = options.callback # TODO: Switch to 'trigger'
@loadComponents(@components)
loadComponents: (components) ->
for componentRef in components
levelComponent = new LevelComponent()
levelComponent = new LevelComponent(componentRef)
url = "/db/level.component/#{componentRef.original}/version/#{componentRef.majorVersion}"
levelComponent.setURL(url)
resource = @supermodel.loadModel levelComponent, 'component'
continue unless resource.isLoading
@listenToOnce resource, 'loaded', ->
return if @handlingChange
if @supermodel.finished()
@handlingChange = true
@onComponentsAdded()
@handlingChange = false
@handlingChange = true
@onComponentsAdded()
@handlingChange = false
afterRender: ->
super()
return unless @supermodel.finished()
@buildExtantComponentsTreema()
@buildComponentsTreema()
@addThangComponentConfigViews()
buildExtantComponentsTreema: ->
buildComponentsTreema: ->
treemaOptions =
supermodel: @supermodel
schema: Level.schema.properties.thangs.items.properties.components
@ -50,8 +53,8 @@ module.exports = class ThangComponentsEditView extends CocoView
nodeClasses:
'thang-components-array': ThangComponentsArrayNode
@extantComponentsTreema = @$el.find('#extant-components-column .treema').treema treemaOptions
@extantComponentsTreema.build()
@componentsTreema = @$el.find('#thang-components-column .treema').treema treemaOptions
@componentsTreema.build()
onComponentsTreemaChanged: =>
return if @handlingChange
@ -61,7 +64,7 @@ module.exports = class ThangComponentsEditView extends CocoView
componentMap[component.original] = component
newComponentsList = []
for component in @extantComponentsTreema.data
for component in @componentsTreema.data
newComponentsList.push(componentMap[component.original] or component)
@components = newComponentsList
@ -75,14 +78,10 @@ module.exports = class ThangComponentsEditView extends CocoView
# * if components are added externally, like by a modal
# * if a dependency loads and is added to the list
# TODO: Disallow editing components in the list, otherwise this system breaks.
if @components.length < @lastComponentLength
@onComponentsRemoved()
else
@onComponentsAdded()
@lastComponentLength = @components.length
onComponentsRemoved: ->
componentMap = {}
@ -122,9 +121,10 @@ module.exports = class ThangComponentsEditView extends CocoView
@reportChanges()
updateComponentsList: ->
@extantComponentsTreema?.set('/', $.extend(true, [], @components))
@componentsTreema?.set('/', $.extend(true, [], @components))
onComponentsAdded: ->
return unless @componentsTreema
componentMap = {}
for component in @components
componentMap[component.original] = component
@ -158,6 +158,7 @@ module.exports = class ThangComponentsEditView extends CocoView
# Sort the component list, reorder the component config views
@updateComponentsList()
@addThangComponentConfigViews()
@checkForMissingSystems()
@reportChanges()
addThangComponentConfigViews: ->
@ -171,7 +172,7 @@ module.exports = class ThangComponentsEditView extends CocoView
# Put back config views into the DOM based on the component list ordering,
# adding and registering new ones as needed.
configsEl = @$el.find('#thang-component-configs')
for componentRef in @extantComponentsTreema.data
for componentRef in @componentsTreema.data
subview = componentConfigViews[componentRef.original]
if not subview
subview = @makeThangComponentConfigView(componentRef)
@ -201,7 +202,7 @@ module.exports = class ThangComponentsEditView extends CocoView
@reportChanges()
onSelectComponent: (e, nodes) =>
@extantComponentsTreema.$el.find('.dependent').removeClass('dependent')
@componentsTreema.$el.find('.dependent').removeClass('dependent')
return unless nodes.length is 1
# find dependent components
@ -220,7 +221,7 @@ module.exports = class ThangComponentsEditView extends CocoView
componentsToCheck.push otherComponentRef.original
# highlight them
for child in _.values(@extantComponentsTreema.childrenTreemas)
for child in _.values(@componentsTreema.childrenTreemas)
if dependents[child.data.original]
child.$el.addClass('dependent')
@ -240,30 +241,41 @@ module.exports = class ThangComponentsEditView extends CocoView
@buildAddComponentTreema()
@reportChanges()
onAddComponentEnterPressed: (node) =>
# TODO: Incorporate this logic when adding components
if extantSystems
extantSystems =
(@supermodel.getModelByOriginalAndMajorVersion LevelSystem, sn.original, sn.majorVersion).attributes.name.toLowerCase() for idx, sn of @level.get('systems')
requireSystem = node.data.system.toLowerCase()
if requireSystem not in extantSystems
warn_element = 'Component <b>' + node.data.name + '</b> requires system <b>' + requireSystem + '</b> which is currently not specified in this level.'
checkForMissingSystems: ->
return unless @level
extantSystems =
(@supermodel.getModelByOriginalAndMajorVersion LevelSystem, sn.original, sn.majorVersion).attributes.name.toLowerCase() for idx, sn of @level.get('systems')
componentModels = (@supermodel.getModelByOriginal(LevelComponent, c.original) for c in @components)
componentSystems = (c.get('system') for c in componentModels when c)
for system in componentSystems
if system not in extantSystems
s = "Component requires system <strong>#{system}</strong> which is currently not included in this level."
noty({
text: warn_element,
text: s,
layout: 'bottomLeft',
type: 'warning'
})
reportChanges: ->
@callback?($.extend(true, [], @components))
@lastComponentLength = @components.length
@trigger 'components-changed', $.extend(true, [], @components)
# TODO: Fix these.
undo: ->
if @configView is null or @configView?.editing is false then @extantComponentsTreema.undo() else @configView.undo()
undo: -> @componentsTreema.undo()
redo: ->
if @configView is null or @configView?.editing is false then @extantComponentsTreema.redo() else @configView.redo()
redo: -> @componentsTreema.redo()
onAddComponentsButtonClicked: ->
modal = new AddThangComponentsModal({skipOriginals: (c.original for c in @components)})
@openModalView modal
@listenToOnce modal, 'hidden', ->
componentsToAdd = modal.getSelectedComponents()
sparseComponents = ({original: c.get('original'), majorVersion: c.get('version').major} for c in componentsToAdd)
@loadComponents(sparseComponents)
@components = @components.concat(sparseComponents)
@onComponentsChanged()
class ThangComponentsArrayNode extends TreemaArrayNode
valueClass: 'treema-thang-components-array'

View file

@ -1,5 +1,5 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/editor/level/thang/edit'
template = require 'templates/editor/level/thang/level-thang-edit-view'
ThangComponentsEditView = require 'views/editor/component/ThangComponentsEditView'
ThangType = require 'models/ThangType'
@ -10,7 +10,7 @@ module.exports = class LevelThangEditView extends CocoView
ThangType editor view.
###
id: 'editor-level-thang-edit'
id: 'level-thang-edit-view'
template: template
events:
@ -41,9 +41,9 @@ module.exports = class LevelThangEditView extends CocoView
supermodel: @supermodel
level: @level
world: @world
callback: @onComponentsChanged
@thangComponentEditView = new ThangComponentsEditView options
@listenTo @thangComponentEditView, 'components-changed', @onComponentsChanged
@insertSubView @thangComponentEditView
thangTypeNames = (m.get('name') for m in @supermodel.getModels ThangType)
input = @$el.find('#thang-type-link input').autocomplete(source: thangTypeNames, minLength: 0, delay: 0, autoFocus: true)

View file

@ -81,8 +81,9 @@ module.exports = class ThangTypeEditView extends RootView
options =
components: @thangType.get('components') ? []
supermodel: @supermodel
callback: @onComponentsChanged
@thangComponentEditView = new ThangComponentsEditView options
@listenTo @thangComponentEditView, 'components-changed', @onComponentsChanged
@insertSubView @thangComponentEditView
onComponentsChanged: (components) =>

View file

@ -0,0 +1,57 @@
ThangComponentEditView = require('views/editor/component/ThangComponentsEditView')
SuperModel = require('models/SuperModel')
LevelComponent = require('models/LevelComponent')
responses =
'/db/level.component/B/version/0': {
system: 'System'
original: 'B'
majorVersion: 0
name: 'B (depends on A)'
dependencies: [{original:'A', majorVersion: 0}]
}
'/db/level.component/A/version/0': {
system: 'System'
original: 'A'
majorVersion: 0
name: 'A'
configSchema: { type: 'object', properties: { propA: { type: 'number' }, propB: { type: 'string' }} }
}
componentC = new LevelComponent({
system: 'System'
original: 'C'
majorVersion: 0
name: 'C (depends on B)'
dependencies: [{original:'B', majorVersion: 0}]
})
describe 'ThangComponentsEditView', ->
view = null
beforeEach ->
supermodel = new SuperModel()
supermodel.registerModel(componentC)
view = new ThangComponentEditView({ components: [], supermodel: supermodel })
view.render()
view.componentsTreema.set('/', [ { original: 'C', majorVersion: 0 }])
spyOn(window, 'noty')
it 'loads dependencies when you add a component with the left side treema', ->
success = jasmine.Ajax.requests.sendResponses(responses)
expect(success).toBeTruthy()
expect(_.size(view.subviews)).toBe(3)
it 'adds dependencies to its components list', ->
jasmine.Ajax.requests.sendResponses(responses)
componentOriginals = (c.original for c in view.components)
expect('A' in componentOriginals).toBeTruthy()
expect('B' in componentOriginals).toBeTruthy()
expect('C' in componentOriginals).toBeTruthy()
it 'removes components that are dependent on a removed component', ->
jasmine.Ajax.requests.sendResponses(responses)
view.components = (c for c in view.components when c.original isnt 'A')
view.onComponentsChanged()
expect(view.components.length).toBe(0)
expect(_.size(view.subviews)).toBe(0)

View file

@ -0,0 +1,160 @@
AddThangComponentsModal = require('views/editor/component/AddThangComponentsModal')
response =
[
{
"_id": "53c46b06bd135abdb79a4f32",
"name": "InventorySomething",
"original": "53c46ae2bd135abdb79a4f2f",
"official": false,
"configSchema": {
"additionalProperties": false,
"type": "object"
},
"propertyDocumentation": [],
"dependencies": [],
"code": "class AttacksSelf extends Component\n @className: 'AttacksSelf'\n chooseAction: ->\n @attack @",
"description": "This Component makes the Thang attack itself.",
"system": "inventory",
"version": {
"minor": 2,
"major": 0,
"isLatestMajor": true,
"isLatestMinor": true
}
},
{
"_id": "538755f3cb18e70000712278",
"name": "Jitters",
"original": "530d8a70286ddc0000cc5d9d",
"official": false,
"configSchema": {
"additionalProperties": false,
"type": "object"
},
"propertyDocumentation": [],
"dependencies": [],
"code": "class AttacksSelf extends Component\n @className: 'AttacksSelf'\n chooseAction: ->\n @attack @",
"description": "This Component makes the Thang jitter. Or, it would, if it did anything yet. (Test Component.)",
"system": "movement",
"version": {
"minor": 3,
"major": 0,
"isLatestMajor": true,
"isLatestMinor": true
}
},
{
"_id": "538755f3cb18e70000712279",
"name": "DelaysExistence",
"original": "524cbbea3ea855e0ab00003d",
"official": false,
"configSchema": {
"additionalProperties": false,
"type": "object"
},
"propertyDocumentation": [],
"dependencies": [],
"code": "class AttacksSelf extends Component\n @className: 'AttacksSelf'\n chooseAction: ->\n @attack @",
"description": "This Thang doesn't show up right away.",
"system": "existence",
"version": {
"minor": 25,
"major": 0,
"isLatestMajor": true,
"isLatestMinor": true
}
},
{
"_id": "538755f3cb18e7000071227a",
"name": "RunsInCircles",
"original": "52438245ef76c3dcf5000004",
"official": false,
"configSchema": {
"additionalProperties": false,
"type": "object"
},
"propertyDocumentation": [],
"dependencies": [],
"code": "class AttacksSelf extends Component\n @className: 'AttacksSelf'\n chooseAction: ->\n @attack @",
"description": "This Thang runs in circles.",
"system": "ai",
"version": {
"minor": 39,
"major": 0,
"isLatestMajor": true,
"isLatestMinor": true
}
},
{
"_id": "538755f3cb18e7000071227b",
"name": "AttacksSelf",
"original": "52437b061d9e25b8dc000004",
"official": false,
"configSchema": {
"additionalProperties": false,
"type": "object"
},
"propertyDocumentation": [],
"dependencies": [],
"code": "class AttacksSelf extends Component\n @className: 'AttacksSelf'\n chooseAction: ->\n @attack @",
"description": "This Component makes the Thang attack itself.",
"system": "ai",
"version": {
"minor": 41,
"major": 0,
"isLatestMajor": true,
"isLatestMinor": true
}
},
{
"_id": "538755f3cb18e7000071227d",
"name": "FollowsNearestFriend",
"original": "52437e31ef76c3dcf5000002",
"official": false,
"configSchema": {
"additionalProperties": false,
"type": "object"
},
"propertyDocumentation": [],
"dependencies": [],
"code": "class AttacksSelf extends Component\n @className: 'AttacksSelf'\n chooseAction: ->\n @attack @",
"description": "This Thang follows the nearest friend.",
"system": "ai",
"version": {
"minor": 39,
"major": 0,
"isLatestMajor": true,
"isLatestMinor": true
}
},
{
"_id": "538755f4cb18e7000071227e",
"name": "FollowsNearest",
"original": "52437c851d9e25b8dc000008",
"official": false,
"configSchema": {
"additionalProperties": false,
"type": "object"
},
"propertyDocumentation": [],
"dependencies": [],
"code": "class AttacksSelf extends Component\n @className: 'AttacksSelf'\n chooseAction: ->\n @attack @",
"description": "This Thang follows the nearest other Thang.",
"system": "ai",
"version": {
"minor": 39,
"major": 0,
"isLatestMajor": true,
"isLatestMinor": true
}
}
]
module.exports = ->
view = new AddThangComponentsModal({skipOriginals:['52437c851d9e25b8dc000008']}) # FollowsNearest original
console.log jasmine.Ajax.requests.all()
jasmine.Ajax.requests.mostRecent().response({status: 200, responseText: JSON.stringify(response)})
view.render()
return view

View file

@ -0,0 +1,40 @@
ThangComponentEditView = require('views/editor/component/ThangComponentsEditView')
responses =
'/db/level.component/A/version/0': {
system: 'System'
original: 'A'
majorVersion: 0
name: 'A'
configSchema: { type: 'object', properties: { propA: { type: 'number' }, propB: { type: 'string' }} }
}
'/db/level.component/B/version/0': {
system: 'System'
original: 'B'
majorVersion: 0
name: 'B (depends on A)'
dependencies: [{original:'A', majorVersion: 0}]
}
'/db/level.component/C/version/0': {
system: 'System'
original: 'C'
majorVersion: 0
name: 'C (depends on B)'
dependencies: [{original:'B', majorVersion: 0}]
}
module.exports = ->
view = new ThangComponentEditView({
components: [
{ original: 'A', majorVersion: 0, config: {propA: 1, propB: 'string'} }
{ original: 'B', majorVersion: 0 }
{ original: 'C', majorVersion: 0 }
]
})
view.render()
jasmine.Ajax.requests.sendResponses(responses)
view.$el.css('background', 'white')
return view