coffeescript-ui
Version:
Coffeescript User Interface System
430 lines (347 loc) • 10.5 kB
text/coffeescript
###
* coffeescript-ui - Coffeescript User Interface System (CUI)
* Copyright (c) 2013 - 2016 Programmfabrik GmbH
* MIT Licence
* https://github.com/programmfabrik/coffeescript-ui, http://www.coffeescript-ui.org
###
CUI.Template.loadTemplateText(require('./ItemList.html'));
class CUI.ItemList extends CUI.VerticalLayout
init: ->
super()
("cui-item-list")
= new CUI.Template(name: "item-list-body")
(, "center")
if
()
initOpts: ->
super()
items:
mandatory: true
check: (v) ->
CUI.util.isFunction(v) or CUI.util.isArray(v)
# if set no "null", don't manage this for us
# otherwise ItemList sets the active item
# according to the active idx
# set to -1, if you want manage, but nothing can be highlighted
active_item_idx:
check: "Integer"
# use this if your items method
# returns a Deferred, the
# method of ItemList will then
# return immediately and not run
# "@_items()"
has_items:
default: false
check: Boolean
allow_null:
check: Boolean
onClick:
check: Function
onActivate:
check: Function
onDeactivate:
check: Function
orientation:
default: "vertical"
check: ["horizontal","vertical"]
keyboardControl:
check: Boolean
default: false
@
readOpts: ->
super()
if == "horizontal"
console.error("new CUI.ItemList, orientation == horizontal needs implementation!")
@
setActiveIdx: () ->
getActiveIdx: ->
getBody: ->
getItemByValue: (value) ->
for el in .DOM.children
btn = CUI.dom.data(el, "element")
if btn not instanceof CUI.Button
continue
if btn.getValue() == value
return btn
null
# returns immediately, if "Promise" we assume yes
hasItems: (event) ->
if
return true
items = (event)
if CUI.util.isPromise(items)
console.warn("ItemList.hasItems: opts.items Function returned a Promise. Set opts.has_items to true, in order to avoid the call of that Function.")
true
else
items.length > 0
__getItems: (event) ->
if CUI.util.isFunction()
(event, @) or []
else
getItems: (event) ->
items = (event)
if CUI.util.isPromise(items)
items
else
new CUI.Deferred().resolve(items)
__initActiveIdx: ->
active_idx =
if active_idx == undefined
items = ()
if CUI.util.isPromise(items)
active_idx = -1
else
for item, idx in items
if not item
continue
if CUI.util.isUndef(item.active)
continue
if item.active
active_idx = idx
break
# we only get here, if any
# of out items has an
# "active" property
if active_idx == undefined
active_idx = -1
if active_idx != null
if active_idx == -1
= null
else
= active_idx
= "item-list--"+()
# console.debug "active:", active_idx,
= true
render: (menu, event) ->
if not
()
.empty()
promise = (event)
promise.done (items) =>
opt_keys = CUI.defaults.class.Button.getOptKeys()
list_has_button_left = false
for _item, idx in items
if CUI.util.isFunction(_item)
item = _item(@, menu, event)
else
item = _item
do (item, idx) =>
if not item or item.hidden == true or item.hidden?()
return
if item.divider
divider = CUI.dom.div("cui-menu-divider cui-item-list-divider", role: "menu-item")
# DOM.data(divider[0], "itemListIdx", idx)
.append(divider)
return
if item.label
if item.label instanceof CUI.Label
label = item.label
else if CUI.util.isPlainObject(item.label)
label = new CUI.defaults.class.Label(item.label)
else
label = new CUI.defaults.class.Label(text: item.label)
# DOM.data(label.DOM[0], "itemListIdx", idx)
label.addClass("cui-menu-item")
.append(label)
return
if item.content
# console.debug "adding item content", item.content
.append(item.content)
return
# console.debug "output item:", , , idx, item
listenButtonClick = (btn) =>
CUI.Events.listen
type: "cui-button-click"
node: btn
call: (ev, info) =>
?(info.event, btn, item, idx)
if not menu?.isAutoCloseAfterClick() or btn.hasMenu()
return
hide = =>
menu.hideAll(info.event)
el = menu.getElement()
if el
dim = CUI.dom.getDimensions(menu.getElement())
if dim.clientWidth == 0 and dim.clientHeight == 0
hide()
return
# we need to do this after a timeout, because
# some buttons have default actions
# which might be interruped if we destroy the button
# width menu.hideAll
CUI.setTimeout(call: hide)
return
if item instanceof CUI.Button
listenButtonClick(item)
if item.hasLeft()
list_has_button_left = true
if item instanceof CUI.Button or
item instanceof CUI.DataField or
item instanceof CUI.Label
.append(item)
return
opts =
role: "menu-item"
radio:
radio_allow_null:
onActivate: (btn, flags) =>
if
= idx
?(btn, item, idx, flags)
()
onDeactivate: (btn, flags) =>
if
= null
?(btn, item, idx, flags)
()
for k in opt_keys
if item.hasOwnProperty(k) and not opts.hasOwnProperty(k)
opts[k] = item[k]
if
if == idx
opts.active = true
if menu
opts.menu_parent = menu
btn = new CUI.defaults.class.Button(opts)
listenButtonClick(btn)
if btn.hasLeft()
list_has_button_left = true
# DOM.data(btn.DOM[0], "itemListIdx", idx)
.append(btn)
return
if list_has_button_left
.addClass("cui-item-list--has-button-left")
else
.removeClass("cui-item-list--has-button-left")
= ()
return
promise
destroy: ->
super()
?.destroy()
__initListeners: ->
= []
preSelectByKeyword = =>
(.join(""))
= []
CUI.Events.listen
type: "item-list-keydown"
node:
call: (_, info) =>
event = info.event
if not CUI.dom.isInDOM()
return
keyboardKey = event.getKeyboardKey()
switch keyboardKey
when "Down"
event.preventDefault()
()
break
when "Up"
event.preventDefault()
()
break
when "Return"
(event)
break
when "Right"
event.preventDefault()
()
break
when "Left"
event.preventDefault()
()
break
else
if keyboardKey
.push(keyboardKey)
CUI.scheduleCallback(ms: 200, call: preSelectByKeyword)
return
CUI.Events.listen
type: "mouseover"
node:
call: (ev) =>
.focus()
item = CUI.dom.parents(ev.getTarget(), ".cui-button-button")[0]
if CUI.dom.hasClass(item, CUI.defaults.class.Button.defaults.active_css_class)
return
()
index = (item)
if not CUI.util.isNull(index)
(index)
return
return
__preSelectByKeyword: (keyword) ->
elementMatches = (element) =>
(element instanceof CUI.Button or element instanceof CUI.Label) and element.getText()?.startsWithIgnoreCase(keyword)
nextIndex = + 1
nextElement = (nextIndex)
if elementMatches(nextElement)
(nextIndex)
return
for item, index in ?.DOM.children
element = CUI.dom.data(item, "element")
if elementMatches(element)
(index)
break
return
__activatePreSelected: (event) ->
itemToActivate = ()
if itemToActivate instanceof CUI.Button
itemToActivate.onClickAction(event)
CUI.dom.removeClass(itemToActivate, CUI.defaults.class.Button.defaults.active_css_class)
__preActivateNextButton: ->
if CUI.util.isNull()
index = 0
else
index = + 1
(index)
__preActivatePreviousButton: ->
if CUI.util.isNull()
items = ()
index = items.length - 1
else
index = - 1
(index)
__preActivateButtonByIndex: (newPreActiveIndex) ->
buttonToPreActivate = (newPreActiveIndex)
if buttonToPreActivate instanceof CUI.Button
()
= newPreActiveIndex
CUI.dom.addClass(buttonToPreActivate, CUI.defaults.class.Button.defaults.active_css_class)
CUI.dom.scrollIntoView(buttonToPreActivate)
__deselectPreActivated: ->
buttons = ()
for button in buttons
if CUI.dom.hasClass(button, CUI.defaults.class.Button.defaults.active_css_class)
CUI.dom.removeClass(button, CUI.defaults.class.Button.defaults.active_css_class)
return
__getButtonByIndex: (index) ->
if not or CUI.util.isNull(index) or index < 0
return
item = ()[index]
return CUI.dom.data(item, "element")
__getButtonIndex: (item) ->
for itemButton, index in ()
if item == itemButton
return index
return
__getButtonItems: ->
return Array::filter.call(.DOM.children, (item) =>
element = CUI.dom.data(item, "element")
return element instanceof CUI.Button and element.isEnabled()
)
__initPreActiveIndex: ->
if
return
for item, index in ()
if CUI.dom.hasClass(item, CUI.defaults.class.Button.defaults.active_css_class)
return index
CUI.ready =>
CUI.Events.registerEvent
type: "item-list-keydown"
sink: true