coffeescript-ui
Version:
Coffeescript User Interface System
477 lines (384 loc) • 11.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('./Options.html'));
class CUI.Options extends CUI.DataField
constructor: (opts) ->
super(opts)
if
CUI.dom.addClass(, "cui-options--sortable")
initOpts: ->
super()
radio:
check: (v) ->
CUI.util.isString(v) or CUI.util.isBoolean(v)
radio_unchecked_value:
default: false
min_checked:
check: (v) ->
if
v == 0 or v == 1
else
CUI.util.isInteger(v) and v >= 0
options:
mandatory: true
check: (v) ->
CUI.util.isArray(v) or CUI.util.isFunction(v)
# true: all fields horizontal
# int: n fields horizontal
horizontal:
check: (v) ->
if CUI.util.isBoolean(v) or (CUI.util.isInteger(v) and v > 0)
return true
title:
check: String
activatable:
check: Boolean
sortable:
check: Boolean
sortable_hint:
check: String
placeholder:
default: "No options available."
check: String
columns:
check: (v) ->
if (CUI.util.isInteger(v) and v <= 12) # max 12 columns
return true
default: 1
readOpts: ->
super()
CUI.util.assert(not or not , "new CUI.Options", "opts.sortable and opts.left cannot be used together.", opts: )
CUI.util.assert(not or not , "new CUI.Options", "opts.sortable and opts.radio cannot be used together.", opts: )
CUI.util.assert(not or not .horizontal, "new CUI.Options", "opts.sortable and opts.horizontal cannot be used together.", opts: )
if .horizontal == undefined
= not
if and == undefined
= true
CUI.util.assert(not ( and not ), "new CUI.Options", "opts.sortable needs opts.activatable to be set.", opts: )
getTemplate: ->
if
= new CUI.Template
name: "options-activatable"
map_prefix: "cui-options"
map:
top: true
bottom: true
active: true
inactive: true
else
= new CUI.Template
name: "options"
map:
top: true
bottom: true
center: true
init: ->
switch
when false
= undefined
when true
= "options--"+
else
=
= {}
setData: (data) ->
super(data)
if
if CUI.util.isArray()
= true
else
= false
@
__setDataOnOptions: (init_data=true) ->
if not
return
if init_data
for cb in
cb.callOnOthers("setData", )
if and not
for cb in
[cb.getName()] =
if not init_data
continue
if and JSON.parse() == cb.getOptValue()
cb.setCheckChangedValue(cb.getOptValue())
else
cb.setCheckChangedValue(cb.getOptValueUnchecked())
else
for cb in
opt = cb.getOptValue()
opt_unchecked = cb.getOptValueUnchecked()
if .indexOf(opt) > -1
[cb.getName()] = opt
else
[cb.getName()] = opt_unchecked
if not init_data
continue
if and JSON.parse().indexOf(opt) > -1
cb.setCheckChangedValue(opt)
else
cb.setCheckChangedValue(opt_unchecked)
@
disableOption: (value) ->
cb =
cb.disable()
@
enableOption: (value) ->
cb =
cb.enable()
@
__getCheckboxByValue: (value) ->
found = null
for opt, idx in
if opt.value == value
found = idx
break
CUI.util.assert(found != null, "CUI.Options.__getCheckboxByValue", "Value #{value} not found in CUI.Options.", options: )
[found]
getOptions: ->
setValue: (v, flags={}) ->
flags.__set_on_data = true
super(v, flags)
storeValue: (value, flags={}) ->
super(value, flags)
if flags.__set_on_data
@
displayValue: ->
super()
for cb in
cb.displayValue()
@
checkValue: (_value) ->
if or not
if not CUI.util.isArray(_value)
throw new CUI.CheckValueError("Value must be Array.")
check = _value
else
check = [_value]
for value in check
for opt in
if opt.value == value
return
throw new CUI.CheckValueError("Value is not in the options.")
render: ->
super()
unsorted_options =
sort_options = =>
.sort (a, b) =>
a_idx = CUI.util.idxInArray(CUI.util.idxInArray(a, unsorted_options), )
b_idx = CUI.util.idxInArray(CUI.util.idxInArray(b, unsorted_options), )
CUI.util.compareIndex(a_idx, b_idx)
find_value_in_options = (value, options) ->
for opt, idx in options
if opt.value == value or (not opt.hasOwnProperty("value") and opt.text == value)
return idx
-1
order_value_array = (arr) =>
# store the current index, by doing that
# we make sure that array value which are not among
# our CUI.Options.options, keep their order (but are
# send to the end of the array)
for a, idx in arr
a.___idx = idx
arr.sort (a, b) =>
a_idx = find_value_in_options(a, )
if a_idx == -1
a_idx = arr.length+a.__idx
b_idx = find_value_in_options(b, )
if b_idx == -1
b_idx = arr.length+b.__idx
CUI.util.compareIndex(a_idx, b_idx)
for a, idx in arr
delete(a.___idx)
order_options_by_value_array = =>
if
arr =
else
arr = []
= []
# first put the active values
for value in arr
.push(find_value_in_options(value, unsorted_options))
missing_opts = []
# second put the inactive values
for opt, idx in unsorted_options
if CUI.util.idxInArray(idx, ) == -1
missing_opts.push(opt: opt, idx: idx)
missing_opts.sort (a, b) ->
a_txt = a.opt.text.toLocaleUpperCase()
b_txt = b.opt.text.toLocaleUpperCase()
if a_txt < b_txt
-1
else if a_txt > b_txt
1
else
0
for mopt in missing_opts
.push(mopt.idx)
if and not
order_options_by_value_array()
= unsorted_options.slice(0)
if
sort_options()
= []
= -1
for _opt, idx in
do (_opt) =>
opt = {}
for k, v of _opt
opt[k] = v
CUI.util.assert(not CUI.util.isEmpty(opt.text) or not CUI.util.isEmpty(opt.content), "new #{@__cls}", "opts.options[#{idx}].text|content must be set.", opts: )
if not opt.hasOwnProperty("value")
CUI.util.assert(not CUI.util.isEmpty(opt.text), "new #{@__cls}", "opts.options[#{idx}].text must be set.", opts: )
opt.value = opt.text
chars = opt.text?.length or -1
if < chars
= chars
opt.radio =
if and == 0
if .length == 1
delete(opt.radio)
else
opt.radio_allow_null = true
if not
opt.group = "group-"+
opt.onActivate = (_cb, flags) =>
if _cb.hasData()
if and not
else
CUI.util.pushOntoArray(_cb.getOptValue(), arr = .slice(0))
order_value_array(arr)
if
order_options_by_value_array()
_opt.onActivate?(_cb, flags)
return
opt.onDeactivate = (_cb, flags) =>
if not
c = 0
for f in
if f.isActive()
c++
if c <
return _cb.activate()
if _cb.hasData()
if and not
if not flags.prior_activate
else
CUI.util.removeFromArray(_cb.getOptValue(), arr = .slice(0))
if
order_options_by_value_array()
_opt.onDeactivate?(_cb, flags)
return
opt.undo_support = false
opt.mark_changed =
opt.multiline = true
if
if and not
opt.name =
opt.group =
else
opt.group = "options-"+
opt.name = opt.group+"--"+idx
opt.data =
opt.data_not_for_others = true
cb = new CUI.Checkbox(opt)
CUI.Events.listen
type: "data-changed"
node: cb
call: (ev, info) ->
if info.element == cb
ev.stopImmediatePropagation()
return
.push(cb)
if
if .length
if and not CUI.util.isEmpty()
bottom = new CUI.Label
multiline: true
class: "cui-options-order-hint"
text:
else
bottom = undefined
if not CUI.util.isEmpty()
top = new CUI.Label
class: "cui-options-title"
text:
else
top = undefined
if
else
if > 1
if
else
for cb in
cb.start()
if > 0
cb.setTextMaxChars()
if and cb.isActive()
# we need extra markup around our checkbox
el = CUI.dom.element("DIV", class: "cui-options-sortable-option")
drag_handle = CUI.dom.element("DIV", class: "cui-options-sortable-drag-handle")
drag_handle_inner = CUI.dom.element("DIV", class: "cui-drag-handle-row")
drag_handle.appendChild(drag_handle_inner)
el.appendChild(drag_handle)
el.appendChild(cb.DOM)
else
el = cb.DOM
if
if cb.isActive()
else
else
sortable_element = .map.active
sortable_selector = ".cui-options-sortable-drag-handle"
if
new CUI.Sortable
axis: "y"
element: sortable_element
selector: sortable_selector
sorted: (ev, from_idx, to_idx) =>
CUI.util.moveInArray(from_idx, to_idx, , from_idx < to_idx)
# re order options
sort_options()
# set value to new order
if
arr = .slice(0)
order_value_array(arr)
# re order again by value in case we have sorted
# an inactive
order_options_by_value_array()
else if not CUI.util.isEmpty()
@
getDefaultValue: ->
if and not
else
[]