mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-23 23:58:02 -05:00
Many improvements to the spell palette.
This commit is contained in:
parent
1f7cea4e54
commit
e3824d7698
13 changed files with 159 additions and 46 deletions
|
@ -2,7 +2,7 @@
|
|||
class Vector
|
||||
@className: "Vector"
|
||||
# Class methods for nondestructively operating
|
||||
for name in ['add', 'subtract', 'multiply', 'divide']
|
||||
for name in ['add', 'subtract', 'multiply', 'divide', 'limit', 'normalize']
|
||||
do (name) ->
|
||||
Vector[name] = (a, b, useZ) ->
|
||||
a.copy()[name](b, useZ)
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
margin: 8px 8px 0 0
|
||||
overflow-y: scroll
|
||||
float: left
|
||||
@include user-select(text)
|
||||
|
||||
.prop
|
||||
img
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
left: 10px
|
||||
right: 10px
|
||||
height: 140px
|
||||
padding-top: 50px
|
||||
// Height and padding-top relate to .tab-content height
|
||||
padding-left: 35px
|
||||
padding-right: 60px
|
||||
// Docs' popovers flicker when too far right, so we have big padding-right
|
||||
// twilight: color: #E2E2E2
|
||||
// Height relates to .tab-content height
|
||||
padding-top: 35px
|
||||
padding-left: 12px
|
||||
padding-right: 4px
|
||||
color: #333
|
||||
// Get crazy with the backgrounds so that we can lower the opacity on the editor background above it, making a gradient of the disabled background color on the top around where it's usually covered
|
||||
background-color: transparent
|
||||
|
@ -36,3 +34,23 @@
|
|||
line-height: 16px
|
||||
margin: 0 4px
|
||||
font-weight: normal
|
||||
|
||||
.nav > li > a
|
||||
padding: 2px 20px 0px 20px
|
||||
margin-bottom: 3px
|
||||
|
||||
ul.nav.nav-pills
|
||||
li.active a
|
||||
background-color: transparent
|
||||
&.multiple-tabs li.active a
|
||||
background-color: lighten(rgb(230, 212, 146), 10%)
|
||||
&.multiple-tabs li:not(.active) a
|
||||
cursor: pointer
|
||||
|
||||
//.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus
|
||||
// background-color: lighten(rgb(230, 212, 146), 10%)
|
||||
|
||||
.property-entry-column
|
||||
display: inline-block
|
||||
margin-right: 3px
|
||||
vertical-align: top
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
@import "../../../bootstrap/mixins"
|
||||
@import "app/styles/bootstrap/mixins"
|
||||
@import "app/styles/mixins"
|
||||
|
||||
.spell-palette-entry-view
|
||||
display: inline-block
|
||||
margin: 0px 4px
|
||||
display: block
|
||||
padding: 0px 4px
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace
|
||||
font-size: 12px
|
||||
border: 1px solid transparent
|
||||
cursor: pointer
|
||||
@include user-select(all)
|
||||
|
||||
&:hover
|
||||
border: 1px solid #BFF
|
||||
|
||||
&.pinned
|
||||
background-color: darken(#BFF, 20%)
|
||||
|
||||
// Pulling these colors from the most relevant textmate-theme classes
|
||||
&.function
|
||||
// twilight: color: #7587A6
|
||||
color: #0000A2
|
||||
&.object
|
||||
// twilight: color: #AC885B
|
||||
color: rgb(6, 150, 14)
|
||||
&.string
|
||||
// twilight: color: #CDA869
|
||||
color: rgb(3, 106, 7)
|
||||
&.number
|
||||
// twilight: color: #F9EE98
|
||||
color: rgb(0, 0, 205)
|
||||
&.boolean
|
||||
// twilight: color: #C9CEA8
|
||||
color: rgb(88, 92, 246)
|
||||
&.undefined
|
||||
// twilight: color: #CF6A4C
|
||||
color: rgb(197, 6, 11)
|
||||
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
@import "../../../bootstrap/mixins"
|
||||
@import "app/styles/bootstrap/mixins"
|
||||
@import "app/styles/mixins"
|
||||
|
||||
#tome-view
|
||||
height: 100%
|
||||
|
||||
.popover
|
||||
padding: 10px
|
||||
max-width: 400px
|
||||
min-width: 400px
|
||||
background: transparent url(/images/level/popover_background.png)
|
||||
background-size: 100% 100%
|
||||
border: 0
|
||||
left: auto !important
|
||||
top: auto !important
|
||||
right: 100%
|
||||
bottom: 151px
|
||||
@include box-shadow(0 0 0 #000)
|
||||
@include user-select(text)
|
||||
|
||||
&.pinned
|
||||
@include box-shadow(0 0 500px white)
|
||||
|
||||
h1:not(.not-code), h2:not(.not-code), h3:not(.not-code), h4:not(.not-code), h5:not(.not-code), h6:not(.not-code)
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
img(src="/images/level/code_palette_background.png").code-palette-background
|
||||
h4(data-i18n="play_level.tome_available_spells") Available Spells
|
||||
.properties
|
||||
ul(class="nav nav-pills" + (tabbed ? ' multiple-tabs' : ''))
|
||||
each slug, group in entryGroupSlugs
|
||||
li(class=group == "this" || slug == "available-spells" ? "active" : "")
|
||||
a(data-toggle="pill", data-target='#palette-tab-' + slug)
|
||||
h4= group
|
||||
.tab-content
|
||||
each slug, group in entryGroupSlugs
|
||||
div(id="palette-tab-" + slug, class="tab-pane" + (group == "this" || slug == "available-spells" ? " active" : ""))
|
||||
div(class="properties properties-" + slug)
|
||||
|
|
|
@ -16,7 +16,7 @@ module.exports = class LevelSaveView extends SaveVersionModal
|
|||
constructor: (options) ->
|
||||
super options
|
||||
@level = options.level
|
||||
|
||||
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.level = @level
|
||||
|
@ -63,8 +63,8 @@ module.exports = class LevelSaveView extends SaveVersionModal
|
|||
console.log "Got errors:", JSON.parse(res.responseText)
|
||||
forms.applyErrorsToForm($(form), JSON.parse(res.responseText))
|
||||
res.success =>
|
||||
@hide()
|
||||
modelsToSave = _.without modelsToSave, newModel
|
||||
unless modelsToSave.length
|
||||
url = "/editor/level/#{@level.get('slug') or @level.id}"
|
||||
document.location.href = url
|
||||
@hide() # This will destroy everything, so do it last
|
||||
|
|
|
@ -24,12 +24,15 @@ module.exports = class HUDView extends View
|
|||
'god:new-world-created': 'onNewWorld'
|
||||
|
||||
events:
|
||||
'click': -> Backbone.Mediator.publish 'focus-editor'
|
||||
'click': 'onClick'
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@$el.addClass 'no-selection'
|
||||
|
||||
onClick: (e) ->
|
||||
Backbone.Mediator.publish 'focus-editor' unless $(e.target).parents('.thang-props').length
|
||||
|
||||
onFrameChanged: (e) ->
|
||||
@timeProgress = e.progress
|
||||
@update()
|
||||
|
|
|
@ -64,7 +64,7 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
|
|||
@$el.find('code').popover(
|
||||
animation: true
|
||||
html: true
|
||||
placement: 'bottom'
|
||||
placement: 'left'
|
||||
trigger: 'hover'
|
||||
content: @formatPopover doc
|
||||
container: @$el.parent()
|
||||
|
@ -126,4 +126,5 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
|
|||
|
||||
destroy: ->
|
||||
@avatar?.destroy()
|
||||
@$el.find('code').popover 'destroy'
|
||||
super()
|
||||
|
|
|
@ -4,6 +4,7 @@ popoverTemplate = require 'templates/play/level/tome/spell_palette_entry_popover
|
|||
{me} = require 'lib/auth'
|
||||
filters = require 'lib/image_filter'
|
||||
{downTheChain} = require 'lib/world/world_utils'
|
||||
window.Vector = require 'lib/world/vector' # So we can document it
|
||||
|
||||
# If we use marked somewhere else, we'll have to make sure to preserve options
|
||||
marked.setOptions {gfm: true, sanitize: false, smartLists: true, breaks: true}
|
||||
|
@ -41,16 +42,32 @@ safeJSONStringify = (input, maxDepth) ->
|
|||
output = input
|
||||
JSON.stringify output, null, 1
|
||||
|
||||
# http://stackoverflow.com/a/987376/540620
|
||||
$.fn.selectText = ->
|
||||
el = @[0]
|
||||
if document.body.createTextRange
|
||||
range = document.body.createTextRange()
|
||||
range.moveToElementText(el)
|
||||
range.select()
|
||||
else if window.getSelection
|
||||
selection = window.getSelection()
|
||||
range = document.createRange()
|
||||
range.selectNodeContents(el)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
|
||||
module.exports = class SpellPaletteEntryView extends View
|
||||
tagName: 'div' # Could also try <code> instead of <div>, but would need to adjust colors
|
||||
className: 'spell-palette-entry-view'
|
||||
template: template
|
||||
popoverPinned: false
|
||||
|
||||
subscriptions:
|
||||
'surface:frame-changed': "onFrameChanged"
|
||||
|
||||
events:
|
||||
'mouseover': 'onMouseOver'
|
||||
'mouseenter': 'onMouseEnter'
|
||||
'mouseleave': 'onMouseLeave'
|
||||
'click': 'onClick'
|
||||
|
||||
constructor: (options) ->
|
||||
|
@ -59,14 +76,14 @@ module.exports = class SpellPaletteEntryView extends View
|
|||
@doc = options.doc
|
||||
if _.isString @doc
|
||||
@doc = name: @doc, type: typeof @thang[@doc]
|
||||
@doc.owner ?= 'this'
|
||||
if options.isSnippet
|
||||
@doc.type = 'snippet'
|
||||
@doc.type = @doc.owner = 'snippet'
|
||||
@doc.shortName = @doc.shorterName = @doc.title = @doc.name
|
||||
else
|
||||
@doc.owner ?= 'this'
|
||||
suffix = if @doc.type is 'function' then '()' else ''
|
||||
@doc.shortName = "#{@doc.owner}.#{@doc.name}#{suffix};"
|
||||
if @doc.owner is 'this'
|
||||
if @doc.owner is 'this' or options.tabbify
|
||||
@doc.shorterName = "#{@doc.name}#{suffix}"
|
||||
else
|
||||
@doc.shorterName = @doc.shortName.replace ';', ''
|
||||
|
@ -81,12 +98,12 @@ module.exports = class SpellPaletteEntryView extends View
|
|||
super()
|
||||
@$el.addClass(@doc.type)
|
||||
@$el.popover(
|
||||
animation: true
|
||||
animation: false
|
||||
html: true
|
||||
placement: 'top'
|
||||
trigger: 'hover'
|
||||
placement: 'left'
|
||||
trigger: 'manual' # Hover, until they click, which will then pin it until unclick.
|
||||
content: @formatPopover()
|
||||
container: @$el.parent().parent().parent()
|
||||
container: '#tome-view'
|
||||
)
|
||||
@$el.on 'show.bs.popover', =>
|
||||
Backbone.Mediator.publish 'tome:palette-hovered', thang: @thang, prop: @doc.name
|
||||
|
@ -98,13 +115,14 @@ module.exports = class SpellPaletteEntryView extends View
|
|||
content.replace /\#\{(.*?)\}/g, (s, properties) => @formatValue downTheChain(owner, properties.split('.'))
|
||||
|
||||
formatValue: (v) ->
|
||||
return null if @doc.type is 'snippet'
|
||||
return @thang.now() if @doc.name is 'now'
|
||||
return '[Function]' if not v and @doc.type is 'function'
|
||||
unless v?
|
||||
if @doc.owner is 'this'
|
||||
v = @thang[@doc.name]
|
||||
else
|
||||
v = window[@doc.owner][@doc.name]
|
||||
v = window[@doc.owner][@doc.name] # grab Math or Vector
|
||||
if @doc.type is 'number' and not isNaN v
|
||||
if v == Math.round v
|
||||
return v
|
||||
|
@ -121,12 +139,29 @@ module.exports = class SpellPaletteEntryView extends View
|
|||
return safeJSONStringify v, 2
|
||||
v
|
||||
|
||||
onMouseOver: (e) ->
|
||||
onMouseEnter: (e) ->
|
||||
# Make sure the doc has the updated Thang so it can regenerate its prop value
|
||||
@$el.data('bs.popover').options.content = @formatPopover()
|
||||
@$el.popover('setContent')
|
||||
@$el.popover 'show' unless @popoverPinned
|
||||
|
||||
onMouseLeave: (e) ->
|
||||
@$el.popover 'hide' unless @popoverPinned
|
||||
|
||||
togglePinned: ->
|
||||
if @popoverPinned
|
||||
@popoverPinned = false
|
||||
@$el.add('#tome-view .popover').removeClass 'pinned'
|
||||
@$el.popover 'hide'
|
||||
else
|
||||
@popoverPinned = true
|
||||
@$el.add('#tome-view .popover').addClass 'pinned'
|
||||
|
||||
onClick: (e) ->
|
||||
unless @popoverPinned
|
||||
$(e.target).selectText()
|
||||
e.stopPropagation() # don't re-focus editor since we might want to select text
|
||||
@togglePinned()
|
||||
Backbone.Mediator.publish 'tome:palette-clicked', thang: @thang, prop: @doc.name
|
||||
|
||||
onFrameChanged: (e) ->
|
||||
|
@ -134,5 +169,8 @@ module.exports = class SpellPaletteEntryView extends View
|
|||
@options.thang = @thang = e.selectedThang # Update our thang to the current version
|
||||
|
||||
destroy: ->
|
||||
$('.popover.pinned').remove() if @popoverPinned # @$el.popover('destroy') doesn't work
|
||||
@togglePinned() if @popoverPinned
|
||||
@$el.popover 'destroy'
|
||||
@$el.off()
|
||||
super()
|
||||
|
|
|
@ -5,6 +5,8 @@ filters = require 'lib/image_filter'
|
|||
SpellPaletteEntryView = require './spell_palette_entry_view'
|
||||
LevelComponent = require 'models/LevelComponent'
|
||||
|
||||
N_ROWS = 4
|
||||
|
||||
module.exports = class SpellPaletteView extends View
|
||||
id: 'spell-palette-view'
|
||||
template: template
|
||||
|
@ -18,28 +20,56 @@ module.exports = class SpellPaletteView extends View
|
|||
constructor: (options) ->
|
||||
super options
|
||||
@thang = options.thang
|
||||
@createPalette()
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.entryGroups = @entryGroups
|
||||
c.entryGroupSlugs = @entryGroupSlugs
|
||||
c.tabbed = _.size(@entryGroups) > 1
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@createPalette()
|
||||
for group, entries of @entryGroups
|
||||
groupSlug = @entryGroupSlugs[group]
|
||||
for columnNumber, entryColumn of entries
|
||||
col = $('<div class="property-entry-column"></div>').appendTo @$el.find(".properties-#{groupSlug}")
|
||||
for entry in entryColumn
|
||||
col.append entry.el
|
||||
entry.render() # Render after appending so that we can access parent container for popover
|
||||
|
||||
createPalette: ->
|
||||
lcs = @supermodel.getModels LevelComponent
|
||||
allDocs = {}
|
||||
allDocs[doc.name] = doc for doc in (lc.get('propertyDocumentation') ? []) for lc in lcs
|
||||
|
||||
props = @thang.programmableProperties ? []
|
||||
snippets = @thang.programmableSnippets ? []
|
||||
props = _.sortBy @thang.programmableProperties ? []
|
||||
snippets = _.sortBy @thang.programmableSnippets ? []
|
||||
shortenize = props.length + snippets.length > 6
|
||||
tabbify = props.length + snippets.length >= 10
|
||||
@entries = []
|
||||
@entries.push @addEntry(allDocs[prop] ? prop, shortenize) for prop in props
|
||||
@entries.push @addEntry(allDocs[prop] ? prop, shortenize, true) for prop in snippets
|
||||
@entries.push @addEntry(allDocs[prop] ? prop, shortenize, tabbify) for prop in props
|
||||
@entries.push @addEntry(allDocs[prop] ? prop, shortenize, tabbify, true) for prop in snippets
|
||||
@entries = _.sortBy @entries, (entry) ->
|
||||
order = ['this', 'Math', 'Vector', 'snippets']
|
||||
index = order.indexOf entry.doc.owner
|
||||
index = String.fromCharCode if index is -1 then order.length else index
|
||||
index += entry.doc.name
|
||||
if tabbify and _.find @entries, ((entry) -> entry.doc.owner isnt 'this')
|
||||
@entryGroups = _.groupBy @entries, (entry) -> entry.doc.owner
|
||||
else
|
||||
defaultGroup = $.i18n.t("play_level.tome_available_spells", defaultValue: "Available Spells")
|
||||
@entryGroups = {}
|
||||
@entryGroups[defaultGroup] = @entries
|
||||
@entryGroupSlugs = {}
|
||||
for group, entries of @entryGroups
|
||||
@entryGroupSlugs[group] = _.string.slugify group
|
||||
@entryGroups[group] = _.groupBy entries, (entry, i) -> Math.floor i / N_ROWS
|
||||
null
|
||||
|
||||
addEntry: (doc, shortenize, isSnippet=false) ->
|
||||
entry = new SpellPaletteEntryView doc: doc, thang: @thang, shortenize: shortenize, isSnippet: isSnippet
|
||||
@$el.find('.properties').append entry.el
|
||||
entry.render() # Render after appending so that we can access parent container for popover
|
||||
entry
|
||||
addEntry: (doc, shortenize, tabbify, isSnippet=false) ->
|
||||
new SpellPaletteEntryView doc: doc, thang: @thang, shortenize: shortenize, tabbify: tabbify, isSnippet: isSnippet
|
||||
|
||||
onDisableControls: (e) -> @toggleControls e, false
|
||||
onEnableControls: (e) -> @toggleControls e, true
|
||||
|
|
|
@ -51,7 +51,7 @@ module.exports = class TomeView extends View
|
|||
|
||||
events:
|
||||
'click #spell-view': 'onSpellViewClick'
|
||||
'click': -> Backbone.Mediator.publish 'focus-editor'
|
||||
'click': 'onClick'
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
|
@ -140,6 +140,9 @@ module.exports = class TomeView extends View
|
|||
onSpellViewClick: (e) ->
|
||||
@spellList.$el.hide()
|
||||
|
||||
onClick: (e) ->
|
||||
Backbone.Mediator.publish 'focus-editor' unless $(e.target).parents('.popover').length
|
||||
|
||||
clearSpellView: ->
|
||||
@spellView?.dismiss()
|
||||
@spellView?.$el.after('<div id="' + @spellView.id + '"></div>').detach()
|
||||
|
|
|
@ -387,7 +387,6 @@ module.exports = class PlayLevelView extends View
|
|||
team = team?.team unless _.isString team
|
||||
team ?= 'humans'
|
||||
me.team = team
|
||||
console.log "level:team-set to", team
|
||||
Backbone.Mediator.publish 'level:team-set', team: team
|
||||
|
||||
destroy: ->
|
||||
|
|
Loading…
Reference in a new issue