coffeescript-ui
Version:
Coffeescript User Interface System
689 lines (542 loc) • 16 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
###
class CUI.SimpleForm extends CUI.DataField
initOpts: ->
super()
fields:
mandatory: true
check: (v) ->
CUI.util.isFunction(v) or CUI.util.isArray(v)
header:
check: Array
# true: all fields horizontal
# int: n fields horizontal
horizontal:
default: false
check: (v) ->
CUI.util.isBoolean(v) or (CUI.util.isInteger(v) and v > 0)
appearance:
default: "normal"
mandatory: true
check: ["normal","separators"]
render_as_grid:
default: false
mandatory: true
check: Boolean
blockLevelOffset:
check: "Integer"
default: 0
readOpts: ->
super()
if == 1
= null
else
=
if ?.checkbox
# the form has a checkbox (for form context)
CUI.util.assert(CUI.util.isPlainObject(.checkbox, "new CUI.Form", "opts.form.checkbox needs to be PlainObject.", opts: ))
CUI.util.assert(, "new CUI.Form", "opts.form.checkbox requires opts.name to be set.", opts: )
CUI.util.assert(not .checkbox.data, "new CUI.Form", "opts.form.checkbox cannot have 'data' set.", opts: )
CUI.util.assert(not .checkbox.name, "new CUI.Form", "opts.form.checkbox cannot have 'name' set.", opts: )
cb_opts = CUI.util.copyObject(.checkbox, true)
cb_opts.data = = checkbox: false
cb_opts.name = "checkbox"
onActivate = cb_opts.onActivate
cb_opts.onActivate = (cb, flags, ev) =>
onActivate?(cb, flags, ev)
if not ev.ctrlKey()
return
for cb_el in CUI.dom.matchSelector(, ".cui-checkbox")
CUI.dom.data(cb_el).element.activate()
return
= new CUI.Checkbox(cb_opts).start()
CUI.Events.listen
type: "data-changed"
node:
call: =>
if not
return
if .checkbox
[] =
else
delete([])
else
= null
initLayout: ->
getCheckbox: ->
__createFields: ->
fs = []
fields =
for field, idx in fields
if not field
continue
if CUI.util.isFunction(field)
_field = CUI.DataField.new(field(@))
else
_field = CUI.DataField.new(field)
_field.setForm(@)
fs.push(_field)
fs
getNameOpt: ->
name:
check: String
init: ->
#
initFields: ->
=
reload: ->
super()
displayValue: ->
if
.displayValue()
super()
getParentData: ->
or
setData: (data) ->
if and
CUI.util.assert(not CUI.util.isFunction(data), "Form.setData", "opts.data cannot be set by Function when data is managed by opts.form.checkbox.", opts: )
if and not CUI.util.isFunction(data)
# console.debug "init data ", , data, 1
#
if
= data
if data[]
= data[]
.checkbox = true
#
else
= {}
.checkbox = false
#
super()
else
if CUI.util.isUndef(data[])
data[] = {}
= data
super(data[])
else
super(data)
# sometimes fields depend on data, we need to see if
# that is the case
# these functions do this:
#
# fields: ->
# if not df.getData()
# return []
# else
# return [...]
#
# it can happen, like in FormPopover, that fields
# are not yet initialized
#
if CUI.util.isFunction() and and .length == 0
# console.debug "fields depends on data...",
@
# this hides a form row, if all
# datafields in it are hidden
__setRowVisibility: (tr) ->
df = CUI.dom.data(tr, "data-field")
if not df
console.warn("Form.__setRowVisibility", "data-field not found", df, @)
return
for _f in df.getAllDataFields()
if not _f.isHidden()
CUI.dom.showElement(tr)
return
CUI.dom.hideElement(tr)
return
render: ->
# we need fields rendered before we render
# us, so that <label for=...> can work, just
# to be safe, for "ng" only until we know
# it does not break anything
super()
if not or ?.getValue() == false
CUI.dom.hideElement()
else
CUI.dom.showElement()
@
getLayout: ->
@
renderTable: ->
.empty()
add_listener = (node) =>
CUI.Events.listen
node: node
type: "form-check-row-visibility"
call: (ev) =>
tr = CUI.dom.closest(ev.getNode(), ".cui-form-tr,.cui-form-block,.cui-form-row")
ev.stopPropagation()
if tr
return
# form_depth =
table = null
table_has_left = null
append = (stuff, to) =>
if not to
add_listener(stuff)
else if stuff
to.appendChild(stuff)
return
get_append = (v, info=@) =>
if v instanceof CUI.Form
v.DOM
else if CUI.util.isPlainObject(v) # assume a label constructor
# new CUI.Label(v).DOM
new CUI.MultilineLabel(v).DOM
else if CUI.util.isString(v)
# new CUI.Label(text: v).DOM
new CUI.MultilineLabel(text: v).DOM
else if v?.DOM
v.DOM
else if CUI.util.isFunction(v)
get_append(v(info))
else if CUI.util.isEmpty(v)
null
else
v
get_label = (field, register = false) =>
lbl = field._form?.label
if not lbl
return
if CUI.util.isString(lbl)
label = CUI.dom.element("label")
label.textContent = lbl
field.registerLabel(label)
return label
return get_append(lbl)
fields =
len = fields.length
field_idx = -1
field_has_left = (_field) =>
fopts = _field?._form or {}
if fopts.label
return true
if fopts.use_field_as_label
console.error("Form: use_field_as_label is obsolete in \"ng\" design", @)
return true
return false
render_next_field = =>
field_idx = field_idx + 1
if field_idx == len
# we are done
return
field = fields[field_idx]
hint_div = null
grid = field._form?.grid
if field._form and (not CUI.util.isNull(field._form.hint) or field._form.right)
add_hint_div = =>
if hint_div
return
hint_div = CUI.dom.element("DIV", class: "cui-form-hint", "data-for-field": field.getUniqueId())
if not CUI.util.isNull(field._form.hint)
add_hint_div()
if CUI.util.isString(field._form.hint)
hint_div.appendChild(new CUI.Label(class: "cui-form-hint-label", icon: field._form.hint_icon, text: field._form.hint, multiline: true, markdown: true).DOM)
else
CUI.dom.append(hint_div, field._form.hint)
if field._form.right
add_hint_div()
console.error("Form.renderTable: form.right is deprecated. Use 'hint' instead. Form:", @, "Field:", field, "Field#", field_idx)
# append deprecated stuff to the hint div
# you should use ".hint" instead
append(get_append(field._form.right), hint_div)
if field.renderAsBlock()
level = + 1
if level > 3
level = 3
if field instanceof CUI.Form
cb = field.getCheckbox()
else
cb = null
if cb
do (cb, field) =>
CUI.Events.listen
type: "data-changed"
node: cb
call: =>
if cb.getValue()
CUI.dom.addClass(blk.DOM, "cui-form-block--checkbox-checked")
CUI.dom.showElement(field.DOM)
else
CUI.dom.removeClass(blk.DOM, "cui-form-block--checkbox-checked")
CUI.dom.hideElement(field.DOM)
left_side = cb
else
left_side = get_label(field)
if left_side?.nodeName == "LABEL"
left_side.classList.add("cui-block-title")
blk = new CUI.Block
padded: false
maximize:
maximize_horizontal:
maximize_vertical:
class: "cui-form-block"
level: level
header: left_side
content: [
get_append(field)
hint_div
]
if cb
CUI.dom.addClass(blk.DOM, "cui-form-block--has-checkbox")
if cb.getValue()
CUI.dom.addClass(blk.DOM, "cui-form-block--checkbox-checked")
else
CUI.dom.removeClass(blk.DOM, "cui-form-block--checkbox-checked")
append(blk)
# used to set row visibility
CUI.dom.data(blk.DOM, "data-field", field)
table = null
table_has_left = null
render_next_field()
return
if not table
# check if next subform has a left side
#
has_left = false
for idx in [field_idx...len] by 1
if field_has_left(fields[idx])
has_left = true
break
if not has_left
table = CUI.dom.element("DIV", class: "cui-form-container")
table_has_left = false
else
table = CUI.dom.element("DIV", class: "cui-form-table")
table_has_left = true
if
CUI.dom.addClass(table, "cui-form--horizontal")
# CUI.dom.setAttribute(table, "cui-form-depth", form_depth)
append(table)
name = field.getName()
classes = []
if name
classes.push("cui-form-field-name--"+name)
if field instanceof CUI.Select
classes.push("cui-form-field-type--select")
else if field instanceof CUI.Checkbox
classes.push("cui-form-field-type--checkbox")
else if field instanceof CUI.Input
classes.push("cui-form-field-type--input")
if table_has_left
tr = CUI.dom.element("DIV", class: "cui-form-tr "+classes.join(" "), "data-for-field": field.getUniqueId())
td = CUI.dom.element("DIV", class: "cui-form-td cui-form-key")
append(get_label(field, true), td)
tr.appendChild(td)
td = CUI.dom.element("DIV", class: "cui-form-td cui-form-value")
append(get_append(field), td)
append(hint_div, td)
tr.appendChild(td)
# used to set row visibility
CUI.dom.data(tr, "data-field", field)
if grid
tr.setAttribute("data-cui-grid", grid)
table.appendChild(tr)
else
ff = field._form
if ff
if ff.maximize
classes.push("cui-maximize")
if (ff.maximize and ff.maximize_horizontal != false) or
ff.maximize_horizontal == true
classes.push("cui-maximize-horizontal")
if (ff.maximize and ff.maximize_vertical != false) or
ff.maximize_vertical == true
classes.push("cui-maximize-vertical")
row = CUI.dom.element("DIV", class: "cui-form-row "+classes.join(" "), "data-for-field": field.getUniqueId())
row.appendChild(get_append(field))
append(get_append(field), row)
append(hint_div, row)
# used to set row visibility
CUI.dom.data(row, "data-field", field)
if grid
row.setAttribute("data-cui-grid", grid)
table.appendChild(row)
render_next_field()
render_next_field()
return
renderAsBlock: ->
if
return true
if == false
return false
if == true
return true
fields =
for f in fields
if f._form?.label
return true
if f instanceof CUI.Form
if f.renderAsBlock()
return true
return false
hasContentForAppend: ->
if ?.length > 0
true
else
false
appendToContainer: (stuff) ->
onDataChanged: (ev, info) ->
super(ev, info)
if not info.element
return
# console.debug "Form data-changed",
if info.action in ["goto", "reset"]
return
.log[++.idx] =
name: info.element.getName()
undo_idx: info.undo_idx
action: info.action
.log.splice(.idx+1)
return
hasUserData: (data) ->
for f in
if f.hasUserData(data)
return true
return false
remove: ->
super()
__initUndo: ->
if
return
=
log: []
idx: -1
getLog: ->
log = []
for l in .log
log.push("#{l.name} #{l.action} #{l.undo_idx}")
log.join("\n")
undo: ->
if .idx == -1
return false
if .idx == 0
# we dont exactly know which
# field we need to tell to
for df in
df.goto(.idx--)
return true
l = .log[--.idx]
# console.debug ""+, l.name, .getFieldsByName(l.name)
for f in .getFieldsByName(l.name)
f.goto(l.undo_idx)
return true
redo: ->
if .idx == .log.length - 1
return false
l = .log[++.idx]
for f in .getFieldsByName(l.name)
f.goto(l.undo_idx)
return true
getFieldByIdx: (idx) ->
CUI.util.assert(CUI.util.isInteger(idx) and idx >= 0, "#{@__cls}.getFieldByIdx", "idx must be Integer.", idx: idx)
[idx]
updateHint: (field_name, hint, trigger_resize = true) ->
field = [0]
if not field
console.error("Form.updateHint:", field_name, "not found.")
return
els = CUI.dom.matchSelector(.DOM, ".cui-form-hint[data-for-field='"+field.getUniqueId()+"'] > .cui-form-hint-label")
if els.length != 1
console.error("Form.updateHint:", field_name, "not found in DOM.")
return
CUI.dom.data(els[0]).element.setText(hint)
if trigger_resize
CUI.Events.trigger
type: "content-resize"
node: els[0]
@
__setClassOnField: (field_name, cls, add_remove) ->
for field in
row = CUI.dom.closest(field.DOM, "[data-for-field]")
if not row
continue
if add_remove
CUI.dom.addClass(row, cls)
else
CUI.dom.removeClass(row, cls)
@
addClassToField: (field_name, cls) ->
removeClassFromField: (field_name, cls) ->
getFieldsByName: (name, found_fields = []) ->
CUI.util.assert(CUI.util.isString(name), "#{@__cls}.getFieldsByName", "name must be String.", name: name)
# console.debug , @, typeof()
for _field in
# console.debug _field, CUI.util.getObjectClass(_field), name, _field.getName
if _field.getName() == name
found_fields.push(_field)
if _field instanceof CUI.Form
_field.getFieldsByName(name, found_fields)
# if found_fields.length == 0
# console.warn("#{@__cls}.getFieldsByName: No field found matching \"#{name}\".")
return found_fields
getFields: (func) ->
# console.debug "form get fields",
isDataField: ->
if
return true
else
return false
hasData: ->
false
getValue: ->
# console.debug "getValue FORM", ,
if not
data = {}
for field in
if k = field.getName()
data[k] = field.getValue()
return data
else
# console.debug "getValue HENK", ,
data = CUI.util.copyObject(, true)
delete(data._undo)
return data
isChanged: ->
for _field in
if _field.isChanged()
return true
return false
getBlockLevelOffset: ->
if
parent = (.getBlockLevelOffset?() or 0) + 1
else
parent = 0
return + parent
setBlockLevelOffset: () ->
return
destroy: ->
if
CUI.dom.remove()
= null
super()
CUI.Events.registerEvent
type: "form-check-row-visibility"
bubble: true