296 lines
8.3 KiB
JavaScript
296 lines
8.3 KiB
JavaScript
|
|
Elm.Native.Graphics.Input = function(elm) {
|
|
"use strict";
|
|
|
|
elm.Native = elm.Native || {};
|
|
elm.Native.Graphics = elm.Native.Graphics || {};
|
|
if (elm.Native.Graphics.Input) return elm.Native.Graphics.Input;
|
|
|
|
var Render = ElmRuntime.use(ElmRuntime.Render.Element);
|
|
var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
|
|
|
|
var Signal = Elm.Signal(elm);
|
|
var newElement = Elm.Graphics.Element(elm).newElement;
|
|
var JS = Elm.Native.JavaScript(elm);
|
|
var Utils = Elm.Native.Utils(elm);
|
|
var Tuple2 = Utils.Tuple2;
|
|
|
|
function dropDown(values) {
|
|
var entries = JS.fromList(values);
|
|
var events = Signal.constant(entries[0]._1);
|
|
|
|
var drop = newNode('select');
|
|
drop.style.border = '0 solid';
|
|
for (var i = 0; i < entries.length; ++i) {
|
|
var option = newNode('option');
|
|
var name = JS.fromString(entries[i]._0);
|
|
option.value = name;
|
|
option.innerHTML = name;
|
|
drop.appendChild(option);
|
|
}
|
|
drop.addEventListener('change', function() {
|
|
elm.notify(events.id, entries[drop.selectedIndex]._1);
|
|
});
|
|
|
|
var t = drop.cloneNode(true);
|
|
t.style.visibility = "hidden";
|
|
|
|
elm.node.appendChild(t);
|
|
var style = window.getComputedStyle(t, null);
|
|
var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
|
|
var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
|
|
elm.node.removeChild(t);
|
|
|
|
console.log(w,h);
|
|
var element = A3(newElement, w, h, {
|
|
ctor: 'Custom',
|
|
type: 'DropDown',
|
|
render: function render(model) { return drop; },
|
|
update: function update(node, oldModel, newModel) {},
|
|
model: {}
|
|
});
|
|
|
|
return Tuple2(Signal.constant(element), events);
|
|
}
|
|
|
|
function buttons(defaultValue) {
|
|
var events = Signal.constant(defaultValue);
|
|
|
|
function render(model) {
|
|
var b = newNode('button');
|
|
b.style.display = 'block';
|
|
b.elmEvent = model.event;
|
|
function click() { elm.notify(events.id, b.elmEvent); }
|
|
b.addEventListener('click', click);
|
|
b.innerHTML = model.text;
|
|
return b;
|
|
}
|
|
|
|
function update(node, oldModel, newModel) {
|
|
node.elmEvent = newModel.event;
|
|
var txt = newModel.text;
|
|
if (oldModel.text !== txt) node.innerHTML = txt;
|
|
}
|
|
|
|
function button(evnt, txt) {
|
|
return A3(newElement, 100, 40, {
|
|
ctor: 'Custom',
|
|
type: 'Button',
|
|
render: render,
|
|
update: update,
|
|
model: { event:evnt, text:JS.fromString(txt) }
|
|
});
|
|
}
|
|
|
|
return { _:{}, button:F2(button), events:events };
|
|
}
|
|
|
|
function customButtons(defaultValue) {
|
|
var events = Signal.constant(defaultValue);
|
|
|
|
function render(model) {
|
|
var btn = newNode('div');
|
|
btn.elmEvent = model.event;
|
|
|
|
btn.elmUp = Render.render(model.up);
|
|
btn.elmHover = Render.render(model.hover);
|
|
btn.elmDown = Render.render(model.down);
|
|
|
|
function replace(node) {
|
|
if (node !== btn.firstChild) btn.replaceChild(node, btn.firstChild);
|
|
}
|
|
var overCount = 0;
|
|
function over(e) {
|
|
if (overCount++ > 0) return;
|
|
replace(btn.elmHover);
|
|
}
|
|
function out(e) {
|
|
if (btn.contains(e.toElement || e.relatedTarget)) return;
|
|
overCount = 0;
|
|
replace(btn.elmUp);
|
|
}
|
|
function up() {
|
|
replace(btn.elmHover);
|
|
elm.notify(events.id, btn.elmEvent);
|
|
}
|
|
function down() { replace(btn.elmDown); }
|
|
btn.addEventListener('mouseover', over);
|
|
btn.addEventListener('mouseout' , out);
|
|
btn.addEventListener('mousedown', down);
|
|
btn.addEventListener('mouseup' , up);
|
|
|
|
btn.appendChild(btn.elmUp);
|
|
return btn;
|
|
}
|
|
|
|
function update(node, oldModel, newModel) {
|
|
node.elmEvent = newModel.event;
|
|
Render.update(node.elmUp, oldModel.up, newModel.up)
|
|
Render.update(node.elmHover, oldModel.hover, newModel.hover)
|
|
Render.update(node.elmDown, oldModel.down, newModel.down)
|
|
}
|
|
|
|
function button(evnt, up, hover, down) {
|
|
return A3(newElement,
|
|
Math.max(up.props.width, hover.props.width, down.props.width),
|
|
Math.max(up.props.height, hover.props.height, down.props.height),
|
|
{ ctor: 'Custom',
|
|
type: 'CustomButton',
|
|
render: render,
|
|
update: update,
|
|
model: { event:evnt, up:up, hover:hover, down:down }
|
|
});
|
|
}
|
|
|
|
return { _:{}, customButton:F4(button), events:events };
|
|
}
|
|
|
|
|
|
function hoverables(defaultValue) {
|
|
var events = Signal.constant(defaultValue);
|
|
function hoverable(handler, elem) {
|
|
function onHover(bool) {
|
|
elm.notify(events.id, handler(bool));
|
|
}
|
|
var props = Utils.replace([['hover',onHover]], elem.props);
|
|
return { props:props, element:elem.element };
|
|
}
|
|
return { _:{}, hoverable:F2(hoverable), events:events };
|
|
}
|
|
|
|
|
|
function checkboxes(defaultValue) {
|
|
var events = Signal.constant(defaultValue);
|
|
|
|
function render(model) {
|
|
var b = newNode('input');
|
|
b.type = 'checkbox';
|
|
b.checked = model.checked;
|
|
b.style.display = 'block';
|
|
b.elmHandler = model.handler;
|
|
function change() { elm.notify(events.id, b.elmHandler(b.checked)); }
|
|
b.addEventListener('change', change);
|
|
return b;
|
|
}
|
|
|
|
function update(node, oldModel, newModel) {
|
|
node.elmHandler = newModel.handler;
|
|
node.checked = newModel.checked;
|
|
return true;
|
|
}
|
|
|
|
function box(handler, checked) {
|
|
return A3(newElement, 13, 13, {
|
|
ctor: 'Custom',
|
|
type: 'CheckBox',
|
|
render: render,
|
|
update: update,
|
|
model: { checked:checked, handler:handler }
|
|
});
|
|
}
|
|
|
|
return { _:{}, box:F2(box), events:events };
|
|
}
|
|
|
|
function setRange(node, start, end, dir) {
|
|
if (node.parentNode) {
|
|
node.setSelectionRange(start, end, dir);
|
|
} else {
|
|
setTimeout(function(){node.setSelectionRange(start, end, dir);}, 0);
|
|
}
|
|
}
|
|
|
|
function mkTextPool(type) { return function fields(defaultValue) {
|
|
var events = Signal.constant(defaultValue);
|
|
|
|
var state = null;
|
|
|
|
function render(model) {
|
|
var field = newNode('input');
|
|
field.elmHandler = model.handler;
|
|
|
|
field.id = 'test';
|
|
field.type = type;
|
|
field.placeholder = JS.fromString(model.placeHolder);
|
|
field.value = JS.fromString(model.state.string);
|
|
setRange(field, model.state.selectionStart, model.state.selectionEnd, 'forward');
|
|
field.style.border = 'none';
|
|
state = model.state;
|
|
|
|
function update() {
|
|
var start = field.selectionStart,
|
|
end = field.selectionEnd;
|
|
if (field.selectionDirection === 'backward') {
|
|
start = end;
|
|
end = field.selectionStart;
|
|
}
|
|
state = { _:{},
|
|
string:JS.toString(field.value),
|
|
selectionStart:start,
|
|
selectionEnd:end };
|
|
elm.notify(events.id, field.elmHandler(state));
|
|
}
|
|
function mousedown() {
|
|
update();
|
|
elm.node.addEventListener('mouseup', mouseup);
|
|
}
|
|
function mouseup() {
|
|
update();
|
|
elm.node.removeEventListener('mouseup', mouseup)
|
|
}
|
|
field.addEventListener('keyup', update);
|
|
field.addEventListener('mousedown', mousedown);
|
|
|
|
return field;
|
|
}
|
|
|
|
function update(node, oldModel, newModel) {
|
|
node.elmHandler = newModel.handler;
|
|
if (state === newModel.state) return;
|
|
var newStr = JS.fromString(newModel.state.string);
|
|
if (node.value !== newStr) node.value = newStr;
|
|
|
|
var start = newModel.state.selectionStart;
|
|
var end = newModel.state.selectionEnd;
|
|
var direction = 'forward';
|
|
if (end < start) {
|
|
start = end;
|
|
end = newModel.state.selectionStart;
|
|
direction = 'backward';
|
|
}
|
|
|
|
if (node.selectionStart !== start
|
|
|| node.selectionEnd !== end
|
|
|| node.selectionDirection !== direction) {
|
|
setRange(node, start, end, direction);
|
|
}
|
|
}
|
|
|
|
function field(handler, placeHolder, state) {
|
|
return A3(newElement, 200, 30,
|
|
{ ctor: 'Custom',
|
|
type: type + 'Input',
|
|
render: render,
|
|
update: update,
|
|
model: { handler:handler,
|
|
placeHolder:placeHolder,
|
|
state:state }
|
|
});
|
|
}
|
|
|
|
return { _:{}, field:F3(field), events:events };
|
|
}
|
|
}
|
|
|
|
return elm.Native.Graphics.Input = {
|
|
buttons:buttons,
|
|
customButtons:customButtons,
|
|
hoverables:hoverables,
|
|
checkboxes:checkboxes,
|
|
fields:mkTextPool('text'),
|
|
emails:mkTextPool('email'),
|
|
passwords:mkTextPool('password'),
|
|
dropDown:dropDown
|
|
};
|
|
|
|
};
|