Redesign the crafting page

This introduces the new crafting page design featuring a new vertical
layout and list display of all crafting steps. It also makes it easy
to move items between the have/need sections and even from each step
into your inventory (causing a new crafting plan to be created which
doesn't plan on making that item).

* Convert the `BaseController.hide` and `show` methods to be much
  more robust in the use of CSS transitions (using a timer in case a
  transition doesn't actually fire).
* Change `hide`/`show` to use events for communicating animation
  state instead of callbacks
* Convert several controllers to fire events for communication with
  parent controllers instead of accepting functions.
* Rewrite the `CraftPageController`, `CraftPage` model and related
  Jade/SCSS files to implement the new design.
* Alter the `InventoryController` and `StackController` to allow a
  variety of action buttons, and for the events from those action
  buttons to bubble up to parent controllers
* Add the `StepController` and its related model, template, and style
  sheet as part of the new craft page.
* Tweak the `ItemSelectorController` and related files to allow for
  a variety of styles for the launcher button
This commit is contained in:
Andrew Miner
2015-04-20 09:17:58 -07:00
parent daaeb523d9
commit 5b014c9921
61 changed files with 855 additions and 401 deletions
Binary file not shown.
+32 -19
View File
@@ -37,32 +37,45 @@ Duration.fast = 200
Duration.normal = 400 Duration.normal = 400
Duration.slow = 1200 Duration.slow = 1200
exports.Event = Event = {} exports.Event = Event = {}
Event.add = 'add' # collection, item... Event.add = 'add' # collection, item...
Event.change = 'change' # model Event.animate = {}
Event.click = 'click' # event Event.animate.hide = {}
Event.load = {} Event.animate.hide.start = 'animate:hide:start' # controller, selector
Event.load.started = 'load:started' # controller, url Event.animate.hide.finish = 'animate:hide:finish' # controller, selector
Event.load.succeeded = 'load:succeeded' # controller, book Event.animate.show = {}
Event.load.failed = 'load:failed' # controller, error message Event.animate.show.start = 'animate:show:start' # controller, selector
Event.load.finished = 'load:finished' # controller Event.animate.show.finish = 'animate:show:finish' # controller, selector
Event.remove = 'remove' # collection, item... Event.button = {}
Event.request = 'request' # model Event.button.complete = 'button:complete' # controller
Event.route = 'route' Event.button.first = 'button:first' # controller, buttonType
Event.sort = 'sort' Event.button.second = 'button:second' # controller, buttonType
Event.sync = 'sync' # model, response Event.change = 'change' # model
Event.transitionEnd = (-> Event.click = 'click' # event
Event.load = {}
Event.load.started = 'load:started' # controller, url
Event.load.succeeded = 'load:succeeded' # controller, book
Event.load.failed = 'load:failed' # controller, error message
Event.load.finished = 'load:finished' # controller
Event.remove = 'remove' # collection, item...
Event.request = 'request' # model
Event.route = 'route'
Event.sort = 'sort'
Event.sync = 'sync' # model, response
Event.transitionEnd = (->
return unless document? return unless document?
transitions = transitions =
'transition': 'transitionend',
'OTransition': 'oTransitionEnd',
'MSTransition': 'msTransitionEnd',
'MozTransition': 'transitionend',
'WebkitTransition': 'webkitTransitionEnd' 'WebkitTransition': 'webkitTransitionEnd'
'MozTransition': 'transitionend'
'MSTransition': 'msTransitionEnd'
'OTransition': 'oTransitionEnd'
'transition': 'transitionend'
el = document.createElement 'fakeelement' el = document.createElement 'fakeelement'
for styleName, eventName of transitions for styleName, eventName of transitions
return eventName if el.style[styleName]? return eventName if el.style[styleName]?
throw new Error 'cannot determine transitionEnd event'
)() )()
exports.Key = Key = {} exports.Key = Key = {}
+52 -44
View File
@@ -5,22 +5,22 @@ Copyright (c) 2014-2015 by Redwood Labs
All rights reserved. All rights reserved.
### ###
views = require '../views' views = require '../views'
{Event} = require '../constants' {Duration} = require '../constants'
{Event} = require '../constants'
######################################################################################################################## ########################################################################################################################
module.exports = class BaseController extends Backbone.View module.exports = class BaseController extends Backbone.View
constructor: (options={})-> constructor: (options={})->
options.useAnimations ?= true
@tryRefresh = _.debounce @_tryRefresh, 100 @tryRefresh = _.debounce @_tryRefresh, 100
@_model = null
@_rendered = false
@_parent = options.parent
@_children = [] @_children = []
@_useAnimations = options.useAnimations @_model = null
@_parent = options.parent
@_rendered = false
@_useAnimations = options.useAnimations ?= true
@_loadTemplate options.templateName @_loadTemplate options.templateName
super options super options
@@ -36,33 +36,39 @@ module.exports = class BaseController extends Backbone.View
@_children.push child @_children.push child
return child return child
hide: (args...)-> hide: ($el)->
{$el, callback} = @_resolveShowHideArgs args $el ?= @$el
$el.addClass 'hideable' unless $el.hasClass 'hideable' if not $el.hasClass 'hideable'
$el.addClass 'hideable'
_.defer => @hide $el
return
logger.verbose => "#{this} is hiding #{$el.selector}" if not ($el.hasClass('hiding') or $el.hasClass('hidden'))
if $el.hasClass('hiding') or $el.hasClass('hidden') logger.verbose => "#{this} is hiding \"#{$el.selector}\""
_.defer => callback this @_onAnimationComplete $el, Duration.normal, =>
else
$el.off Event.transitionEnd
$el.one Event.transitionEnd, =>
$el.addClass 'hidden' $el.addClass 'hidden'
$el.removeClass 'hiding' $el.removeClass 'hiding'
callback this logger.verbose => "#{this} is finished hiding #{$el.selector}"
@trigger Event.animate.hide.finish, this, $el
$el.addClass 'hiding' $el.addClass 'hiding'
@trigger Event.animate.hide.start, this, $el
refresh: -> refresh: ->
logger.verbose => "#{this} refreshing" logger.verbose => "#{this} refreshing"
remove: -> remove: ->
if @_useAnimations if @_useAnimations
@hide => logger.verbose => "#{this} is hiding element before removing it"
@once Event.animate.hide.finish, =>
logger.verbose => "#{this} is removing its element from the DOM" logger.verbose => "#{this} is removing its element from the DOM"
@$el.remove() @$el.remove()
@off()
@hide()
else else
@$el.remove() @$el.remove()
@off()
routeLinkClick: (event)-> routeLinkClick: (event)->
event.preventDefault() event.preventDefault()
@@ -75,19 +81,22 @@ module.exports = class BaseController extends Backbone.View
else else
router.navigate href, trigger:true router.navigate href, trigger:true
show: (args...)-> show: ($el)->
{$el, callback} = @_resolveShowHideArgs args $el ?= @$el
$el.addClass 'hideable'
$el.addClass 'hideable' unless $el.hasClass 'hideable' if $el.hasClass('hidden') or $el.hasClass('hiding')
logger.verbose => "#{this} is showing \"#{$el.selector}\""
logger.verbose => "#{this} is showing #{$el.selector}" @_onAnimationComplete $el, Duration.normal, =>
if $el.hasClass('hiding') or $el.hasClass('hidden') logger.verbose => "#{this} is finished showing #{$el.selector}"
$el.off Event.transitionEnd @trigger Event.animate.show.finish, this, $el
$el.one Event.transitionEnd, => callback this
$el.removeClass 'hiding' $el.addClass 'hiding'
$el.removeClass 'hidden' $el.removeClass 'hidden'
else _.defer =>
_.defer => callback this $el.removeClass 'hiding'
@trigger Event.animate.show.start, this, $el
unrender: -> unrender: ->
@undelegateEvents() @undelegateEvents()
@@ -164,14 +173,12 @@ module.exports = class BaseController extends Backbone.View
$renderedEl = $($(@_template(data))[0]) $renderedEl = $($(@_template(data))[0])
@onWillRender() @onWillRender()
@hide() if @_useAnimations
@unrender() @unrender()
@$el.append $renderedEl.children() @$el.append $renderedEl.children()
@$el.addClass $renderedEl.attr 'class' @$el.addClass $renderedEl.attr 'class'
@$el.data $renderedEl.data() @$el.data $renderedEl.data()
@delegateEvents() @delegateEvents()
@show() if options.show and @_useAnimations
@_rendered = true @_rendered = true
@onDidRender() @onDidRender()
@@ -189,19 +196,20 @@ module.exports = class BaseController extends Backbone.View
if templateName? if templateName?
@_template = views[templateName] @_template = views[templateName]
_resolveShowHideArgs: (args)->
if args.length is 0
return $el:@$el, callback:->
else if args.length is 1
if _.isFunction args[0]
return $el:@$el, callback:args[0]
else
return $el:args[0], callback:->
else if args.length is 2
return $el:args[0], callback:args[1]
else
throw new Error "expected to get 0, 1, or 2 args"
_tryRefresh: -> _tryRefresh: ->
return unless @_rendered return unless @_rendered
@refresh() @refresh()
_onAnimationComplete: ($el, maxDuration, callback)->
$el ?= @$el
maxDuration ?= Duration.slow
callback ?= -> # do nothing
$el.off Event.transitionEnd
$el.one Event.transitionEnd, callback
# One would very much like this not to be necessary, but because of the way CSS transitions work, they are
# frequently not triggered. Rather than try to avoid the multitude of corner cases where this can happen, this
# simply sets a timer to ensure the event does eventually trigger. Since we're only listening for the first
# event, if the transition end event *did* happen, then this is a no-op. -- Andrew 2015-04-18
setTimeout (-> $el.trigger Event.transitionEnd), maxDuration
@@ -5,17 +5,13 @@ Copyright (c) 2014-2015 by Redwood Labs
All rights reserved. All rights reserved.
### ###
CraftingTableController = require './crafting_table_controller' AdsenseController = require './adsense_controller'
CraftPage = require '../models/craft_page' CraftPage = require '../models/craft_page'
{Event} = require '../constants' InventoryController = require './inventory_controller'
ImageLoader = require './image_loader' PageController = require './page_controller'
InventoryController = require './inventory_controller' StepController = require './step_controller'
ModPackController = require './mod_pack_controller' {Event} = require '../constants'
NameFinder = require '../models/name_finder' {Url} = require '../constants'
PageController = require './page_controller'
Storage = require '../models/storage'
{Text} = require '../constants'
{Url} = require '../constants'
######################################################################################################################## ########################################################################################################################
@@ -24,20 +20,56 @@ module.exports = class CraftPageController extends PageController
constructor: (options={})-> constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required' if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required' if not options.modPack? then throw new Error 'options.modPack is required'
if not options.storage? then throw new Error 'options.storage is required'
options.model ?= new CraftPage modPack:options.modPack options.model ?= new CraftPage modPack:options.modPack
options.storage ?= new Storage storage:window.localStorage options.templateName = 'craft_page'
options.templateName = 'craft_page'
super options super options
@imageLoader = options.imageLoader @imageLoader = options.imageLoader
@modPack = options.modPack @modPack = options.modPack
@storage = options.storage @storage = options.storage
@model.plan.on Event.change, => @refresh()
# Event Methods ################################################################################ # Event Methods ################################################################################
onToolsBoxToggled: -> onCraftTool: (itemSlug)->
@model.plan.includingTools = @$('.includeTools:checked').length isnt 0 # TODO: implement this
onHaveInventoryChanged: ->
@storage.store 'crafting-plan:have', @model.plan.have.unparse()
onMoveNeedToHave: (itemSlug)->
quantity = @model.plan.need.quantityOf itemSlug
@model.plan.have.add itemSlug, quantity
@_saveHaveInventory()
onRemoveFromHaveInventory: (itemSlug)->
@model.plan.have.remove itemSlug
@onHaveInventoryChanged()
onRemoveFromWant: (itemSlug)->
@model.plan.want.remove itemSlug
@onWantInventoryChange()
onStopUsingTool: (itemSlug)->
# TODO: implement this
onStepComplete: (stepController)->
step = stepController.model
for stack in step.recipe.output
@model.plan.have.add stack.itemSlug, stack.quantity * step.multiplier
@onHaveInventoryChanged()
onWantInventoryChange: ->
text = @model.plan.want.unparse()
url = Url.crafting inventoryText:text
router.navigate url
if @model.plan.want.isEmpty
@model.plan.have.clear()
@onHaveInventoryChanged()
# PageController Overrides ##################################################################### # PageController Overrides #####################################################################
@@ -46,69 +78,105 @@ module.exports = class CraftPageController extends PageController
# BaseController Overrides ##################################################################### # BaseController Overrides #####################################################################
onDidRender: ->
@adsenseController = @addChild AdsenseController, '.view__adsense', model:'sidebar_skyscraper'
@wantInventoryController = @addChild InventoryController, '.want .view__inventory',
imageLoader: @imageLoader
isAcceptable: (item)=> item.isCraftable
model: @model.plan.want
modPack: @modPack
firstButtonType: 'remove'
@wantInventoryController.on Event.button.first, (c, s)=> @onRemoveFromWant(s)
@wantInventoryController.on Event.change, (c)=> @onWantInventoryChange()
# @toolsInUseController = @addChild InventoryController, '.tools .view__inventory',
# imageLoader: @imageLoader
# model: @model.plan.toolsInUse
# modPack: @modPack
# firstButtonType: 'down'
# secondButtonType: 'up'
# @toolsInUseController.on Event.button.first, (c, s)=> @onStopUsingTool(s)
# @toolsInUseController.of Event.button.second, (c, s)=> @onCraftTool(s)
@haveInventoryController = @addChild InventoryController, '.have .view__inventory',
imageLoader: @imageLoader
model: @model.plan.have
modPack: @modPack
firstButtonType: 'down'
@haveInventoryController.on Event.button.first, (c, s)=>
@onRemoveFromHaveInventory(s)
@haveInventoryController.on Event.change, (c)=> @onHaveInventoryChanged()
@needInventoryController = @addChild InventoryController, '.need .view__inventory',
editable: false
imageLoader: @imageLoader
model: @model.plan.need
modPack: @modPack
firstButtonType: 'up'
@needInventoryController.on Event.button.first, (c, s)=> @onMoveNeedToHave(s)
@$instructionsSection = @$('.instructions.section')
@$makeSection = @$('.make.section')
@$toolsSection = @$('.tools.section')
@$ingredientsSection = @$('.ingredients.section')
@$stepsSection = @$('.steps.section')
@$stepsContainer = @$('.steps.section .panel')
super
onWillRender: -> onWillRender: ->
@storage.register 'crafting-plan', @model.plan, 'includingTools'
@model.plan.have.clear() @model.plan.have.clear()
@model.plan.have.parse @storage.load('crafting-plan:have') @model.plan.have.parse @storage.load('crafting-plan:have')
super super
onDidRender: ->
@wantController = @addChild InventoryController, '.want',
editable: true
icon: '/images/fishing_rod.png'
imageLoader: @imageLoader
isAcceptable: (item)-> item.isCraftable
model: @model.plan.want
modPack: @model.modPack
onChange: => @_updateLocation()
title: 'Items you want'
@haveController = @addChild InventoryController, '.have',
editable: true,
imageLoader: @imageLoader
model: @model.plan.have
modPack: @model.modPack
onChange: => @_saveHaveInventory()
title: 'Items you have'
@needController = @addChild InventoryController, '.need',
editable: false
icon: '/images/boots.png'
imageLoader: @imageLoader
model: @model.plan.need
modPack: @model.modPack
title: "Items you'll need"
@craftingTableController = @addChild CraftingTableController, '.view__crafting_table',
imageLoader: @imageLoader
model: @model.table
modPack: @model.modPack
@$('.want .toolbar').append '<label><input class="includeTools" type="checkbox"> include tools</label>'
@$includeToolsBox = @$('.includeTools')
super
refresh: -> refresh: ->
if @model.plan.includingTools @_refreshSectionVisibility()
@$includeToolsBox.attr 'checked', 'checked' @_refreshSteps()
else
@$includeToolsBox.removeAttr 'checked'
super super
# Backbone.View Overrides ###################################################################### # Backbone.View Overrides ########################################################################
events: -> events: ->
return _.extend super, return _.extend super,
'change .includeTools': 'onToolsBoxToggled' 'click .instructions a': 'routeLinkClick'
# Private Methods ############################################################################## # Private Methods ################################################################################
_saveHaveInventory: -> _isStepCompletable: (controller)->
@storage.store 'crafting-plan:have', @model.plan.have.unparse() return not @model.plan.want.hasAtLeast controller.model.outputItemSlug
_updateLocation: -> _refreshSectionVisibility: ->
text = @model.plan.want.unparse() if @model.plan.want.isEmpty
url = Url.crafting inventoryText:text for $el in [@$toolsSection, @$ingredientsSection, @$stepsSection]
router.navigate url @hide $el
@show @$instructionsSection
else
for $el in [@$toolsSection, @$ingredientsSection, @$stepsSection]
@show $el
@hide @$instructionsSection
_refreshSteps: ->
@_stepControllers ?= []
index = 0
for step in @model.plan.steps
controller = @_stepControllers[index]
if not controller?
controller = new StepController
canComplete: (controller)=> @_isStepCompletable(controller)
imageLoader: @imageLoader
model: step
modPack: @modPack
controller.on Event.button.complete, (c)=> @onStepComplete(c)
controller.render()
@$stepsContainer.append controller.$el
@_stepControllers.push controller
else
controller.model = step
index += 1
while @_stepControllers.length > index
@_stepControllers.pop().remove()
@@ -81,7 +81,7 @@ module.exports = class HeaderController extends BaseController
addThisUrl = '//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-54f3c9717b2e530d' addThisUrl = '//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-54f3c9717b2e530d'
$('body').append "<script async src=\"#{addThisUrl}\"></script>" $('body').append "<script async src=\"#{addThisUrl}\"></script>"
@_itemSelector = @addChild ItemSelectorController, '.view__item_selector', @_itemSelector = @addChild ItemSelectorController, '.view__item_selector.search',
modPack: @modPack modPack: @modPack
onChoseItem: (itemSlug)=> @_goToItem(itemSlug) onChoseItem: (itemSlug)=> @_goToItem(itemSlug)
@@ -16,6 +16,14 @@ StackController = require './stack_controller'
######################################################################################################################## ########################################################################################################################
###
Events:
clear(this)
change(this)
add(this, itemSlug)
button:first(this, itemSlug)
button:second(this, itemSlug)
###
module.exports = class InventoryController extends BaseController module.exports = class InventoryController extends BaseController
@MAX_QUANTITY = 9999 @MAX_QUANTITY = 9999
@@ -27,13 +35,16 @@ module.exports = class InventoryController extends BaseController
if not options.model? then throw new Error 'options.model is required' if not options.model? then throw new Error 'options.model is required'
if not options.modPack? then throw new Error 'options.modPack is required' if not options.modPack? then throw new Error 'options.modPack is required'
@editable = options.editable ?= true @imageLoader = options.imageLoader
@icon = options.icon ?= '/images/chest_front.png' @modPack = options.modPack
@imageLoader = options.imageLoader
@isAcceptable = options.isAcceptable ?= null @editable = options.editable ?= true
@modPack = options.modPack @icon = options.icon ?= '/images/chest_front.png'
@onChange = options.onChange ?= -> # do nothing @firstButtonType = options.firstButtonType ?= null
@title = options.title ?= 'Inventory' @isAcceptable = options.isAcceptable ?= null
@secondButtonType = options.secondButtonType ?= null
@shouldEnableButton = options.shouldEnableButton ?= (model, button)-> true
@title = options.title ?= 'Inventory'
options.templateName = 'inventory' options.templateName = 'inventory'
super options super options
@@ -46,11 +57,19 @@ module.exports = class InventoryController extends BaseController
onClearButtonClicked: -> onClearButtonClicked: ->
@model.clear() @model.clear()
@onChange() @trigger Event.clear, this
@trigger Event.change, this
onFirstButtonClicked: (stackController)->
@trigger Event.button.first, this, stackController?.model?.itemSlug
onItemChosen: (itemSlug)-> onItemChosen: (itemSlug)->
@model.add itemSlug, 1 @model.add itemSlug, 1
@onChange() @trigger Event.add, this, itemSlug
@trigger Event.change, this
onSecondButtonClicked: (stackController)->
@trigger Event.button.second, this, stackController?.model?.itemSlug
# BaseController Overrides ##################################################################### # BaseController Overrides #####################################################################
@@ -60,32 +79,23 @@ module.exports = class InventoryController extends BaseController
modPack: @modPack, modPack: @modPack,
onChoseItem: (itemSlug)=> @onItemChosen(itemSlug) onChoseItem: (itemSlug)=> @onItemChosen(itemSlug)
@$clearButton = @$('button[name="clear"]') @$clearButton = @$('button.clear')
@$icon = @$('.icon') @$icon = @$('.icon')
@$editPanel = @$('.edit') @$itemContainer = @$('.item_container')
@$scrollbox = @$('.scrollbox')
@$table = @$('table')
@$toolbar = @$('.toolbar')
@$title = @$('h2 p') @$title = @$('h2 p')
super super
refresh: -> refresh: ->
if @editable if @editable
@show @$editPanel @selector.show()
@show @$toolbar, =>
@$scrollbox.css bottom:@$toolbar.height()
else else
if not @$editPanel? then throw new Error "no edit panel" @selector.hide()
if not @$toolbar? then throw new Error "no toolbar"
@hide @$editPanel
@hide @$toolbar
@$scrollbox.css bottom:0
@$icon.attr 'src', @icon @$icon.attr 'src', @icon
@$title.html @title @$title.html @title
@$clearButton.disabled = @model.isEmpty
@_refreshStacks() @_refreshStacks()
@_refreshButtonState()
super super
@@ -93,38 +103,37 @@ module.exports = class InventoryController extends BaseController
events: -> events: ->
return _.extend super, return _.extend super,
'click button[name="clear"]': 'onClearButtonClicked' 'click button.clear': 'onClearButtonClicked'
'click button.first': 'onFirstButtonClicked'
'click button.second': 'onSecondButtonClicked'
# Private Methods ############################################################################## # Private Methods ##############################################################################
_refreshButtonState: ->
if @model.isEmpty then @$clearButton.attr('disabled', 'disabled') else @$clearButton.removeAttr('disabled')
_refreshStacks: -> _refreshStacks: ->
@_stackControllers ?= [] @_stackControllers ?= []
index = 0 index = 0
$lastRow = @$table.find 'tr:last-child'
@model.each (stack)=> @model.each (stack)=>
controller = @_stackControllers[index] controller = @_stackControllers[index]
if not controller? if not controller?
controller = new StackController controller = new StackController
editable: @editable editable: @editable
imageLoader: @imageLoader firstButtonType: @firstButtonType
model: stack imageLoader: @imageLoader
modPack: @modPack model: stack
onChange: @onChange modPack: @modPack
onRemove: if not @editable then null else (stack)=> @_removeStack(stack) secondButtonType: @secondButtonType
shouldEnableButton: @shouldEnableButton
controller.on Event.change, => @trigger Event.change, this
controller.on Event.button.first, (c)=> @onFirstButtonClicked(c)
controller.on Event.button.second, (c)=> @onSecondButtonClicked(c)
controller.render()
@_stackControllers.push controller @_stackControllers.push controller
controller.$el.insertBefore $lastRow @$itemContainer.append controller.$el
controller.render()
else else
controller.model = stack controller.model = stack
index += 1 index += 1
while @_stackControllers.length > index while @_stackControllers.length > index
@_stackControllers.pop().remove() @_stackControllers.pop().remove()
_removeStack: (stack)->
@model.remove stack.itemSlug, stack.quantity
@@ -65,7 +65,6 @@ module.exports = class ItemGroupController extends BaseController
_refreshItems: -> _refreshItems: ->
@_itemControllers ?= [] @_itemControllers ?= []
controllerIndex = 0 controllerIndex = 0
delay = 0
for item in @model for item in @model
controller = @_itemControllers[controllerIndex] controller = @_itemControllers[controllerIndex]
@@ -24,6 +24,7 @@ module.exports = class MinimalRecipeController extends BaseController
@imageLoader = options.imageLoader @imageLoader = options.imageLoader
@modPack = options.modPack @modPack = options.modPack
@multiplier = options.multiplier ?= null
# BaseController Overrides ##################################################################### # BaseController Overrides #####################################################################
@@ -36,6 +37,7 @@ module.exports = class MinimalRecipeController extends BaseController
imageLoader: @imageLoader imageLoader: @imageLoader
modPack: @modPack modPack: @modPack
@$multiplier = @$('.multiplier')
@$outputImg = @$('.output img') @$outputImg = @$('.output img')
@$outputLink = @$('.output a') @$outputLink = @$('.output a')
@$outputQuantity = @$('.quantity') @$outputQuantity = @$('.quantity')
@@ -46,6 +48,8 @@ module.exports = class MinimalRecipeController extends BaseController
@gridController.model = @model @gridController.model = @model
@outputSlotController.model = @model?.output?[0] @outputSlotController.model = @model?.output?[0]
@$el.tooltip show:{delay:Duration.snap, duration:Duration.fast} @$el.tooltip show:{delay:Duration.snap, duration:Duration.fast}
@_refreshMultiplier()
@_refreshTools() @_refreshTools()
super super
@@ -57,6 +61,12 @@ module.exports = class MinimalRecipeController extends BaseController
# Private Methods ############################################################################## # Private Methods ##############################################################################
_refreshMultiplier: ->
if @multiplier? and @multiplier > 1
@$multiplier.html "x#{@multiplier}"
else
@$multiplier.html ''
_refreshTools: -> _refreshTools: ->
@$toolContainer.empty() @$toolContainer.empty()
return unless @model? return unless @model?
@@ -159,7 +159,7 @@ module.exports = class ModPageController extends PageController
@$tutorialsContainer.append controller.$el @$tutorialsContainer.append controller.$el
controller.render() controller.render()
else else
controller.model = model controller.model = tutorial
index += 1 index += 1
+70 -37
View File
@@ -6,13 +6,20 @@ All rights reserved.
### ###
BaseController = require './base_controller' BaseController = require './base_controller'
ImageLoader = require './image_loader'
{Duration} = require '../constants' {Duration} = require '../constants'
{Event} = require '../constants' {Event} = require '../constants'
ImageLoader = require './image_loader'
{Key} = require '../constants' {Key} = require '../constants'
######################################################################################################################## ########################################################################################################################
###
Events:
change(this)
change:quantity(this, oldQuantity, newQuantity)
button:first(this, type)
button:second(this, type)
###
module.exports = class StackController extends BaseController module.exports = class StackController extends BaseController
@MAX_QUANTITY = 9999 @MAX_QUANTITY = 9999
@@ -22,60 +29,67 @@ module.exports = class StackController extends BaseController
if not options.model? then throw new Error 'options.model is required' if not options.model? then throw new Error 'options.model is required'
if not options.modPack? then throw new Error 'options.modPack is required' if not options.modPack? then throw new Error 'options.modPack is required'
options.editable ?= false
options.onChange ?= -> # do nothing
options.onRemove ?= (stack)-> # do nothing
options.tagName = 'tr'
options.templateName = 'stack' options.templateName = 'stack'
super options super options
@editable = options.editable @editable = options.editable ?= false
@modPack = options.modPack @firstButtonType = options.firstButtonType ?= null
@onChange = options.onChange @imageLoader = options.imageLoader
@onRemove = options.onRemove @modPack = options.modPack
@_imageLoader = options.imageLoader @secondButtonType = options.secondButtonType ?= null
@shouldEnableButton = options.shouldEnableButton ?= (model, button)-> true
@modPack.on Event.change, => @tryRefresh() @modPack.on Event.change, => @tryRefresh()
# Event Methods ################################################################################ # Event Methods ################################################################################
onFirstButtonClicked: (event)->
@trigger Event.button.first, this, @firstButtonType
return false
onQuantityFieldBlur: -> onQuantityFieldBlur: ->
return unless @editable return unless @editable
@$bubble.removeClass 'editing'
oldQuantity = @_priorValue
quantityText = @$quantityField.val().trim() quantityText = @$quantityField.val().trim()
if quantityText.length is 0 if quantityText.length is 0
quantity = @_priorValue newQuantity = oldQuantity
else if not quantityText.match /^[0-9]*$/ else if not quantityText.match /^[0-9]*$/
quantity = 1 newQuantity = 1
else else
quantity = parseInt quantityText, 10 newQuantity = parseInt quantityText, 10
if _.isNaN quantity then quantity = 1 if _.isNaN newQuantity then newQuantity = 1
quantity = Math.min quantity, StackController.MAX_QUANTITY newQuantity = Math.min newQuantity, StackController.MAX_QUANTITY
quantity = Math.max 1, quantity newQuantity = Math.max 1, newQuantity
if quantity? if newQuantity? and newQuantity isnt oldQuantity
@$quantityField.val "#{quantity}" @$quantityField.val "#{newQuantity}"
@$quantityField.removeClass 'error', Duration.snap @$bubble.removeClass 'error', Duration.snap
@$quantityField.removeClass 'error-new', Duration.snap @$bubble.removeClass 'error-new', Duration.snap
@model.quantity = quantity @model.quantity = newQuantity
@onChange() @trigger Event.change + ':quantity', this, oldQuantity, newQuantity
@trigger Event.change, this
onQuantityFieldChanged: -> onQuantityFieldChanged: ->
return unless @editable return unless @editable
quantityText = @$quantityField.val().trim() quantityText = @$quantityField.val().trim()
if not quantityText.match /^[0-9]*$/ if not quantityText.match /^[0-9]*$/
@$quantityField.addClass 'error', 0 @$bubble.addClass 'error', 0
@$quantityField.addClass 'error-new', 0 @$bubble.addClass 'error-new', 0
@$quantityField.removeClass 'error-new', Duration.fast @$bubble.removeClass 'error-new', Duration.fast
else else
@$quantityField.removeClass 'error', Duration.snap @$bubble.removeClass 'error', Duration.snap
@$quantityField.removeClass 'error-new', Duration.snap @$bubble.removeClass 'error-new', Duration.snap
onQuantityFieldFocused: -> onQuantityFieldFocused: ->
if @editable if @editable
@$bubble.addClass 'editing'
@_priorValue = @model.quantity @_priorValue = @model.quantity
@$quantityField.val '' @$quantityField.val ''
else else
@@ -87,18 +101,20 @@ module.exports = class StackController extends BaseController
if event.which is Key.Return if event.which is Key.Return
@$quantityField.blur() @$quantityField.blur()
onRemoveClicked: -> onSecondButtonClicked: (event)->
@onRemove @model event.preventDefault()
@onChange() @trigger Event.button.second, this, @secondButtonType
# BaseController Overrides ##################################################################### # BaseController Overrides #####################################################################
onDidRender: -> onDidRender: ->
@$action = @$('.action') @$action = @$('.action')
@$bubble = @$('.bubble')
@$firstButton = @$('button.first')
@$image = @$('.icon img') @$image = @$('.icon img')
@$nameLink = @$('.name a') @$nameLink = @$('.name a')
@$quantityField = @$('.quantity input') @$quantityField = @$('.quantity input')
@$removeButton = @$('button.remove') @$secondButton = @$('button.second')
super super
refresh: -> refresh: ->
@@ -106,19 +122,23 @@ module.exports = class StackController extends BaseController
display = @modPack.findItemDisplay @model.itemSlug display = @modPack.findItemDisplay @model.itemSlug
@_imageLoader.load display.iconUrl, @$image @imageLoader.load display.iconUrl, @$image
@$nameLink.html display.itemName @$nameLink.html display.itemName
@$nameLink.attr 'href', display.itemUrl @$nameLink.attr 'href', display.itemUrl
@$quantityField.val @model.quantity @$quantityField.val @model.quantity
@_updateButtonType @$firstButton, @firstButtonType
@_updateButtonType @$secondButton, @secondButtonType
@$firstButton.prop('disabled', not @shouldEnableButton(@model, 1)) if @firstButtonType?
@$secondButton.prop('disabled', not @shouldEnableButton(@model, 2)) if @secondButtonType?
if @editable if @editable
@$quantityField.removeAttr 'readonly' @$quantityField.removeAttr 'readonly'
@$quantityField.addClass 'editable' @$bubble.addClass 'editable'
else else
@$quantityField.attr 'readonly', 'readonly' @$quantityField.attr 'readonly', 'readonly'
@$quantityField.removeClass 'editable' @$bubble.removeClass 'editable'
@$action.css display:(if @editable then 'table-cell' else 'none')
super super
@@ -127,8 +147,21 @@ module.exports = class StackController extends BaseController
events: -> events: ->
return _.extend super, return _.extend super,
'blur .quantity input': 'onQuantityFieldBlur' 'blur .quantity input': 'onQuantityFieldBlur'
'click button.remove': 'onRemoveClicked'
'click .name a': 'routeLinkClick' 'click .name a': 'routeLinkClick'
'click button.first': 'onFirstButtonClicked'
'click button.second': 'onSecondButtonClicked'
'focus .quantity input': 'onQuantityFieldFocused' 'focus .quantity input': 'onQuantityFieldFocused'
'input .quantity input': 'onQuantityFieldChanged' 'input .quantity input': 'onQuantityFieldChanged'
'keyup .quantity input': 'onQuantityKeyUp' 'keyup .quantity input': 'onQuantityKeyUp'
# Private Methods ##############################################################################
_updateButtonType: ($button, type)->
for currentType in ['check', 'down', 'remove', 'up']
$button.removeClass currentType
if type?
$button.css 'display', null
$button.addClass type
else
$button.css 'display', 'none'
@@ -0,0 +1,75 @@
###
Crafting Guide - step_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
InventoryController = require './inventory_controller'
MinimalRecipeController = require './minimal_recipe_controller'
{Event} = require '../constants'
########################################################################################################################
module.exports = class StepController extends BaseController
constructor: (options={})->
if not options.model? then throw new Error 'options.model is required'
if not options.modPack? then throw new Error 'options.modPack is required'
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
options.templateName = 'step'
super options
@canComplete = options.canComplete or (controller)-> true
@imageLoader = options.imageLoader
@modPack = options.modPack
# Event Methods ################################################################################
onCompleteButtonClicked: (event)->
event.preventDefault()
@trigger Event.button.complete, this
# BaseController Overrides #####################################################################
onDidRender: ->
@inventoryController = @addChild InventoryController, '.view__inventory',
editable: false
imageLoader: @imageLoader
model: @model.inventory
modPack: @modPack
@recipeController = @addChild MinimalRecipeController, '.view__minimal_recipe',
imageLoader: @imageLoader
model: @model.recipe
modPack: @modPack
@$header = @$('h3')
@$completeButton = @$('button.complete')
super
onWillChangeModel: (oldModel, newModel)->
if not newModel? then throw new Error 'model cannot be null'
return super
refresh: ->
itemDisplay = @modPack.findItemDisplay @model.outputItemSlug
@$header.html "#{@model.number}. #{itemDisplay.itemName}"
@inventoryController.model = @model.inventory
@recipeController.model = @model.recipe
@recipeController.multiplier = @model.multiplier
if @canComplete(this)
@$completeButton.removeProp 'disabled'
else
@$completeButton.prop 'disabled', true
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click button.complete': 'onCompleteButtonClicked'
+3 -1
View File
@@ -224,9 +224,11 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
controller.onWillShow() controller.onWillShow()
controller.$el = $pageContent controller.$el = $pageContent
controller.render() controller.render()
controller.show()
if @_controller? if @_controller?
@_controller.hide -> switchToNextController() @_controller.once Event.animate.hide.finish, switchToNextController
@_controller.hide()
else else
switchToNextController() switchToNextController()
+1
View File
@@ -25,6 +25,7 @@ module.exports = class CraftPage extends BaseModel
@modPack.on Event.change, => @_consumeParams() @modPack.on Event.change, => @_consumeParams()
@on Event.change + ':params', => @_consumeParams() @on Event.change + ':params', => @_consumeParams()
@plan.on Event.change, => @trigger Event.change, this
# Private Methods ############################################################################## # Private Methods ##############################################################################
+18 -7
View File
@@ -6,9 +6,10 @@ All rights reserved.
### ###
BaseModel = require './base_model' BaseModel = require './base_model'
{Event} = require '../constants'
Inventory = require './inventory' Inventory = require './inventory'
ItemSlug = require './item_slug' ItemSlug = require './item_slug'
Step = require './step'
{Event} = require '../constants'
######################################################################################################################## ########################################################################################################################
@@ -26,7 +27,7 @@ module.exports = class CraftingPlan extends BaseModel
recraft = _.debounce (=> @craft()), 100 recraft = _.debounce (=> @craft()), 100
for inventory in [@have, @want] for inventory in [@have, @want]
inventory.on 'change', recraft inventory.on Event.change, recraft
@on Event.change + ':includingTools', recraft @on Event.change + ':includingTools', recraft
@@ -140,7 +141,7 @@ module.exports = class CraftingPlan extends BaseModel
@_findSteps inputStack.itemSlug, parentSteps @_findSteps inputStack.itemSlug, parentSteps
logger.verbose -> "adding step for: #{recipe.slug}" logger.verbose -> "adding step for: #{recipe.slug}"
@steps[recipe.slug] = recipe:recipe, itemSlug:item.slug @steps[recipe.slug] = new Step outputItemSlug:item.slug, recipe:recipe
foundValidRecipe = true foundValidRecipe = true
break break
catch error catch error
@@ -166,16 +167,26 @@ module.exports = class CraftingPlan extends BaseModel
return itemSlug return itemSlug
_removeExtraSteps: -> _removeExtraSteps: ->
result = (step for step in @steps when step.multiplier > 0) result = []
number = 1
for step in @steps
if step.multiplier > 0
result.push step
step.number = number
number += 1
@steps = result @steps = result
_resolveNeeds: -> _resolveNeeds: ->
for i in [@steps.length-1..0] by -1 for i in [@steps.length-1..0] by -1
step = @steps[i] step = @steps[i]
recipe = step.recipe step.number = i + 1
outputQuantity = recipe.getQuantityProducedOf step.itemSlug
step.multiplier = Math.ceil(@need.quantityOf(step.itemSlug) / outputQuantity) recipe = step.recipe
outputQuantity = recipe.getQuantityProducedOf step.outputItemSlug
step.multiplier = Math.ceil(@need.quantityOf(step.outputItemSlug) / outputQuantity)
if @includingTools if @includingTools
recipe.eachToolStack (stack)=> recipe.eachToolStack (stack)=>
+9 -4
View File
@@ -20,9 +20,6 @@ module.exports = class Inventory extends BaseModel
attributes.modPack ?= null attributes.modPack ?= null
@clear() @clear()
Object.defineProperties this,
isEmpty: { get:-> @_itemSlugs.length is 0 }
# Class Methods ################################################################################ # Class Methods ################################################################################
@Delimiters = @Delimiters =
@@ -161,6 +158,14 @@ module.exports = class Inventory extends BaseModel
return parts.join Inventory.Delimiters.Stack return parts.join Inventory.Delimiters.Stack
# Property Methods #############################################################################
getIsEmpty: ->
return @_itemSlugs.length is 0
Object.defineProperties @prototype,
isEmpty: { get:@prototype.getIsEmpty }
# Object Overrides ############################################################################# # Object Overrides #############################################################################
toString: -> toString: ->
@@ -185,7 +190,7 @@ module.exports = class Inventory extends BaseModel
stack = @_stacks[itemSlug] stack = @_stacks[itemSlug]
if not stack? if not stack?
stack = new Stack itemSlug:itemSlug, quantity:quantity stack = new Stack itemSlug:itemSlug, quantity:quantity
@listenTo stack, Event.change, => @trigger Event.change, stack @listenTo stack, Event.change, => @trigger Event.change, this
@_stacks[itemSlug] = stack @_stacks[itemSlug] = stack
@_itemSlugs.push itemSlug @_itemSlugs.push itemSlug
@_sort() @_sort()
+1 -1
View File
@@ -14,7 +14,7 @@ module.exports = class ItemSelector extends BaseModel
constructor: (attributes={}, options={})-> constructor: (attributes={}, options={})->
if not options.modPack? then throw new Error 'options.modPack is required' if not options.modPack? then throw new Error 'options.modPack is required'
options.isAcceptable ?= (itemSlug)-> return true # accept everything by default options.isAcceptable ?= (item)-> return true # accept everything by default
super attributes, options super attributes, options
@_isAcceptable = options.isAcceptable @_isAcceptable = options.isAcceptable
+35
View File
@@ -0,0 +1,35 @@
###
Crafting Guide - step.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseModel = require './base_model'
Inventory = require './inventory'
{Event} = require '../constants'
########################################################################################################################
module.exports = class Step extends BaseModel
constructor: (attributes={}, options={})->
if not attributes.outputItemSlug? then throw new Error 'attributes.outputItemSlug is required'
if not attributes.recipe? then throw new Error 'attributes.recipe is required'
attributes.inventory = new Inventory
attributes.number ?= 1
attributes.outputItemSlug ?= null
attributes.multiplier ?= 1
attributes.recipe ?= null
super attributes, options
@_computeInventory()
@on Event.change + ':multiplier', => @_computeInventory()
# Private Methods ##############################################################################
_computeInventory: ->
@inventory.clear()
@recipe.eachInputStack (stack)=>
@inventory.add stack.itemSlug, stack.quantity * @multiplier
+1 -1
View File
@@ -30,4 +30,4 @@
p Craft p Craft
.dot .dot
.view__item_selector: p.placeholder find an item... .view__item_selector.search: p find an item...
+46 -4
View File
@@ -6,8 +6,50 @@
//- //-
.view__craft_page .view__craft_page
.view__inventory.want .sidebar
.view__inventory.have ins.view__adsense
.view__inventory.need
.view__crafting_table .mainBody
.want.section
h2 Items to Make
.panel
.view__inventory.large.editable
//- .tools.section.hideable.hidden
//- h2 Tools
//- .panel
//- .inUse
//- h3 In Use
//- .view__inventory.large
//-
//- .options
//- h3 More Options
//- .view__inventory.large
.ingredients.section.hideable.hidden
h2 Ingredients
.panel
.have
h3 Already In Inventory
.view__inventory.large
.need
h3 Need to Gather
.view__inventory.large
.steps.section.hideable.hidden
h2 Steps
.panel
.instructions.section
h2 Instructions
.panel
p.
Welcome to the Craft page. Here you can get detailed instructions for making any item in your
current mod pack (use the <a href="/configure">Configure</a> page to change it). Making a crafting
plan is very simple:
ol
li Click the "add an item..." button to choose items to craft
li Use the <img src="/images/up.png"> button to mark any items you already possess
li Use the <img src="/images/check.png"> button to complete any step if you already have that item
+3 -10
View File
@@ -6,13 +6,6 @@
//- //-
.view__inventory .view__inventory
h2 .item_container
img.icon .empty_placeholder.hideable.hidden Empty
p .view__item_selector.hideable.hidden.button: p add an item...
.panel
.scrollbox
table
tr.edit.hideable.hidden
td.name(colspan="4", width="*"): .view__item_selector: p.button-label add an item...
.toolbar.hideable.hidden
button(name="clear") clear
+1 -1
View File
@@ -5,7 +5,7 @@
//- All rights reserved. //- All rights reserved.
//- //-
.view__item.hideable.hidden .view__item
a a
table table
tr tr
-2
View File
@@ -6,8 +6,6 @@
//- //-
.view__item_selector .view__item_selector
p.placeholder find items...
.view__item_selector_popup.hideable.hiding .view__item_selector_popup.hideable.hiding
.search .search
input input
+2 -1
View File
@@ -7,6 +7,7 @@
.view__minimal_recipe .view__minimal_recipe
.input .input
table.view__crafting_grid .view__crafting_grid
.tool .tool
.output.view__slot .output.view__slot
.multiplier
+7 -6
View File
@@ -5,9 +5,10 @@
//- All rights reserved. //- All rights reserved.
//- //-
tr.view__stack .view__stack
td.quantity: input .quantity: .bubble: input
td.icon: img(src='') .icon: img(src='')
td.name(width='*'): a .name: a
td.action .action
button.remove &nbsp; button.second &nbsp;
button.first &nbsp;
+12
View File
@@ -0,0 +1,12 @@
//-
//- Crafting Guide - step.jade
//-
//- Copyright (c) 2015 by Redwood Labs
//- All rights reserved.
//-
.view__step
h3
.view__inventory
.view__minimal_recipe
button.complete
+12
View File
@@ -101,12 +101,20 @@ All rights reserved.
font-family: $font-family-header; font-family: $font-family-header;
font-size: $font-size-large; font-size: $font-size-large;
margin-bottom: 0.5em; margin-bottom: 0.5em;
&:first-child {
margin-top: 0;
}
} }
h3 { h3 {
font-family: $font-family-header; font-family: $font-family-header;
font-size: $font-size-normal; font-size: $font-size-normal;
margin: 2em 0 0 0.5em; margin: 2em 0 0 0.5em;
&:first-child {
margin-top: 0;
}
} }
.entry { .entry {
@@ -118,6 +126,10 @@ All rights reserved.
background: white; background: white;
padding: 1em; padding: 1em;
} }
&:first-child {
margin-top: 0;
}
} }
.slideable { .slideable {
+35 -11
View File
@@ -7,20 +7,44 @@ All rights reserved.
.view__craft_page { .view__craft_page {
& > div { .want.section {
display: inline-block; .view__inventory {
margin-top: 2em; position: relative; width: 100%;
padding: 0 2em; }
position: relative;
vertical-align: top;
} }
.view__inventory { width: 50%; height: 30em; } .tools.section {
.options {
margin-top: 4em;
}
}
.view__crafting_table { width: 50%; height: 30em; } .ingredients.section {
.need {
margin-top: 4em;
}
}
.want label { .instructions.section {
position: absolute; top: 50%; left: 0.5em; p {
@include transform(translateY(-50%)); font-size: $font-size-large;
margin-top: 1em;
&:first-child {
margin-top: 0;
}
}
ol {
margin: 2em 3em 0 3em;
li {
font-family: $font-family-normal;
font-size: $font-size-large;
line-height: $font-size-normal + 0.15em;
list-style: decimal;
margin: 0.66em 0 0 2em;
}
}
} }
} }
+1
View File
@@ -27,5 +27,6 @@ All rights reserved.
@import 'mod_selector'; @import 'mod_selector';
@import 'stack'; @import 'stack';
@import 'slot'; @import 'slot';
@import 'step';
@import 'tutorial'; @import 'tutorial';
@import 'video'; @import 'video';
+54 -74
View File
@@ -6,92 +6,72 @@ All rights reserved.
*/ */
.view__inventory { .view__inventory {
min-height: 14em;
width: 100%;
.edit td { .item_container {
padding-top: 1em; position: relative; width: 100%;
}
.panel { .view__stack > div {
position: absolute; top: 3.6em; right: 2em; bottom: 0; left: 2em; height: 4.2em;
background: $color-background-panel; padding: 0 1em 0.5em 0;
text-align: right; vertical-align: middle;
}
.scrollbox { &:last-child {
position: absolute; top: 0em; right: 0em; bottom: 3em; left: 0em; padding: 0 0 0.5em 0;
padding: 0.5em 1em; }
overflow: auto;
}
.toolbar {
position: absolute; right: 0; bottom: 0; left: 0; height: 3.5em;
background: $color-background-toolbar;
border-top: 1px solid $color-gray-light;
}
.view__item_selector {
height: 2.5em; width: 80%;
background: $color-gray-faint;
margin: 0 auto;
transition: background $animate-normal;
&:hover {
background: $color-background-panel;
} }
}
button[name="clear"] { .icon {
position: absolute; bottom: 0.5em; right: 0.5em; img {
} height: 3.6em; width: 3.6em;
}
input {
border: 1px solid $color-gray-light;
}
button:disabled {
background: white;
color: $color-gray-light;
}
table {
width: 100%;
}
td {
height: 2.4em;
vertical-align: middle;
}
td.icon {
width: 4.8em;
padding: 0 1em;
img {
width: 100%; height: auto;
max-height: 4.8em;
} }
}
td.name { .name {
text-align: left;
padding-right: 1em;
input {
width: 100%; width: 100%;
border: 0; }
border-bottom: 1px dashed $color-gray-light;
outline: 0;
&:focus { .action {
border: 1px solid $color-gray-light; white-space: nowrap;
button:first-child {
margin-right: 0.5em;
} }
} }
} }
td.action { .view__item_selector {
width: 4em; position: relative; width: 80%;
margin: 1em auto;
}
&.large {
.item_container {
.view__stack > div {
height: 5em;
}
.quantity {
.bubble {
border-radius: 3em;
input {
font-size: $font-size-large;
}
}
}
.icon {
img {
height: 4.8em; width: 4.8em;
}
}
.name {
a {
font-size: $font-size-large;
font-weight: bold;
}
}
}
} }
} }
+20 -8
View File
@@ -6,30 +6,42 @@ All rights reserved.
*/ */
.view__item_selector { .view__item_selector {
background: $color-background-panel; position: relative; min-height: $font-size-normal * 2; min-width: 20em;
border: 0.1em solid $color-gray-medium; border: 0.1em solid $color-gray-medium;
border-radius: 1.5em; border-radius: 1.5em;
cursor: pointer; cursor: pointer;
padding: 0 0.75em; padding: 0 0.75em;
p { &.search {
position: relative; top: 50%; width: 100%; background: $color-background-panel;
@include transform(translateY(-50%));
&.placeholder { p {
color: $color-gray-medium; color: $color-gray-medium;
font-family: $font-family-input; font-family: $font-family-input;
font-size: $font-size-normal; }
}
&.button {
background: $color-gray-faint;
&:hover {
background: $color-background-panel;
} }
&.button-label { p {
color: $color-active; color: $color-active;
font-family: $font-family-normal; font-family: $font-family-normal;
font-size: $font-size-normal;
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
} }
} }
p {
position: absolute; top: 50%; width: 100%;
@include transform(translateY(-50%));
font-size: $font-size-normal;
}
} }
.view__item_selector_popup { .view__item_selector_popup {
+8 -7
View File
@@ -44,13 +44,6 @@ All rights reserved.
@include transform(translate(-50%, -50%)); @include transform(translate(-50%, -50%));
} }
.multiplier {
position: absolute; bottom: -2.0em; left: 0; right: 0; height: 1.5em;
font-family: $font-family-header;
font-size: $font-size-large;
text-align: center;
}
.quantity { .quantity {
position: absolute; right: 0.1em; bottom: 0; position: absolute; right: 0.1em; bottom: 0;
color: white; color: white;
@@ -59,4 +52,12 @@ All rights reserved.
text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000; text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000;
} }
} }
.multiplier {
position: absolute; top: 50%; right: 0; width: 2.7em;
@include transform(translateY(-50%) translateY(2.5em));
font-family: $font-family-header;
font-size: $font-size-large;
text-align: center;
}
} }
+4
View File
@@ -29,6 +29,10 @@ All rights reserved.
font-size: $font-size-large; font-size: $font-size-large;
} }
} }
.itemGroups {
margin-top: 2em;
}
} }
.view__item { .view__item {
+110 -53
View File
@@ -7,38 +7,36 @@ All rights reserved.
.view__stack { .view__stack {
font-family: $font-family-normal; font-family: $font-family-normal;
display: table-row;
td { & > div {
padding: 0.2em 0; display: table-cell;
vertical-align: middle;
} }
td.quantity { .quantity {
position: relative; .bubble {
width: 5em; border: 0.1em solid $color-gray-light;
border-radius: 1.5em;
input {
position: absolute; top: 50%; height: $font-size-small; width: 100%;
@include transform(translateY(-50%));
border-radius: 0.75em;
background: $color-ice; background: $color-ice;
color: $color-background-panel;
font-family: $font-family-normal;
font-size: $font-size-normal;
font-weight: bold;
text-align: center;
&.editable { input {
&:focus { width: 3em;
background: $color-background-panel; background: none;
color: $color-text; border: none;
font-family: $font-family-input; color: $color-background-panel;
font-weight: normal; font-family: $font-family-normal;
outline: none; font-size: $font-size-normal;
} font-weight: bold;
line-height: $font-size-normal;
outline: none;
text-align: center;
}
&:hover { &.editable:hover, &.editing {
background: $color-background-panel; background: $color-background-panel;
input {
color: $color-text; color: $color-text;
font-family: $font-family-input; font-family: $font-family-input;
font-weight: normal; font-weight: normal;
@@ -47,10 +45,7 @@ All rights reserved.
} }
} }
td.icon { .icon {
width: 4.6em;
padding: 0.2em 0.5em;
img { img {
width: 100%; height: auto; width: 100%; height: auto;
max-height: 4.8em; max-height: 4.8em;
@@ -58,37 +53,99 @@ All rights reserved.
} }
} }
td.name a { .name {
color: $color-text; a {
display: inline; color: $color-text;
font-size: $font-size-normal; font-size: $font-size-normal;
text-decoration: none; text-decoration: none;
&:hover { &:hover {
color: $color-active-hover; color: $color-active-hover;
text-decoration: underline; text-decoration: underline;
}
} }
} }
td.action { .action {
text-align: right; text-align: right;
}
button.remove { button {
opacity: 0; position: relative; width: 3.0em; height: 3.0em;
position: relative; width: 1.8em; height: 1.8em; display: inline-block;
background: url('/images/remove.png') no-repeat center;
border: 0;
outline: none;
&:hover { border: 0;
background: url('/images/remove-hover.png') no-repeat center; font-size: $font-size-small;
} margin: 0;
} outline: none;
padding: 0;
&:hover { &.check {
button.remove { background: url('/images/check.png') no-repeat center;
opacity: 1;
&:active {
background: url('/images/check_active.png') no-repeat center;
}
&:disabled {
background: url('/images/check_disabled.png') no-repeat center !important;
cursor: inherit;
}
&:hover {
background: url('/images/check_hover.png') no-repeat center;
}
}
&.down {
background: url('/images/down.png') no-repeat center;
&:active {
background: url('/images/down_active.png') no-repeat center;
}
&:disabled {
background: url('/images/down_disabled.png') no-repeat center !important;
cursor: inherit;
}
&:hover {
background: url('/images/down_hover.png') no-repeat center;
}
}
&.remove {
background: url('/images/remove.png') no-repeat center;
&:active {
background: url('/images/remove_active.png') no-repeat center;
}
&:disabled {
background: url('/images/remove_disabled.png') no-repeat center !important;
cursor: inherit;
}
&:hover {
background: url('/images/remove_hover.png') no-repeat center;
}
}
&.up {
background: url('/images/up.png') no-repeat center;
&:active {
background: url('/images/up_active.png') no-repeat center;
}
&:disabled {
background: url('/images/up_disabled.png') no-repeat center !important;
cursor: inherit;
}
&:hover {
background: url('/images/up_hover.png') no-repeat center;
}
}
} }
} }
} }
+47
View File
@@ -0,0 +1,47 @@
/*
Crafting Page - step.scss
Copyright (C) 2015 by Redwood Labs
All rights reserved.
*/
.view__step {
position: relative; min-height: 24em;
border-top: 0.1em solid $color-gray-medium;
padding: 1em 0;
&:first-child {
border-top: 0;
}
.view__inventory {
margin: 1em 0 0 3em;
}
.view__minimal_recipe {
position: absolute; top: 2em; right: 4.8em;
}
button.complete {
position: absolute; top: 1em; right: 0; height: 3em; width: 3em;
background: url("/images/check.png") no-repeat center;
border: 0;
font-size: $font-size-small;
outline: none;
&:active {
background: url('/images/check_active.png') no-repeat center;
}
&:disabled {
background: url('/images/check_disabled.png') no-repeat center !important;
cursor: inherit;
}
&:hover {
background: url('/images/check_hover.png') no-repeat center;
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB