on 802be96 help sign in
296 lines 8.5 kB history raw
2Elm.Native.Graphics.Input = function(elm) {
3 "use strict";
5 elm.Native = elm.Native || {};
6 elm.Native.Graphics = elm.Native.Graphics || {};
7 if (elm.Native.Graphics.Input) return elm.Native.Graphics.Input;
9 var Render = ElmRuntime.use(ElmRuntime.Render.Element);
10 var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
12 var Signal = Elm.Signal(elm);
13 var newElement = Elm.Graphics.Element(elm).newElement;
14 var JS = Elm.Native.JavaScript(elm);
15 var Utils = Elm.Native.Utils(elm);
16 var Tuple2 = Utils.Tuple2;
18 function dropDown(values) {
19 var entries = JS.fromList(values);
20 var events = Signal.constant(entries[0]._1);
22 var drop = newNode('select');
23 drop.style.border = '0 solid';
24 for (var i = 0; i < entries.length; ++i) {
25 var option = newNode('option');
26 var name = JS.fromString(entries[i]._0);
27 option.value = name;
28 option.innerHTML = name;
29 drop.appendChild(option);
30 }
31 drop.addEventListener('change', function() {
32 elm.notify(events.id, entries[drop.selectedIndex]._1);
33 });
35 var t = drop.cloneNode(true);
36 t.style.visibility = "hidden";
38 elm.node.appendChild(t);
39 var style = window.getComputedStyle(t, null);
40 var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
41 var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
42 elm.node.removeChild(t);
44 console.log(w,h);
45 var element = A3(newElement, w, h, {
46 ctor: 'Custom',
47 type: 'DropDown',
48 render: function render(model) { return drop; },
49 update: function update(node, oldModel, newModel) {},
50 model: {}
51 });
53 return Tuple2(Signal.constant(element), events);
54 }
56 function buttons(defaultValue) {
57 var events = Signal.constant(defaultValue);
59 function render(model) {
60 var b = newNode('button');
61 b.style.display = 'block';
62 b.elmEvent = model.event;
63 function click() { elm.notify(events.id, b.elmEvent); }
64 b.addEventListener('click', click);
65 b.innerHTML = model.text;
66 return b;
67 }
69 function update(node, oldModel, newModel) {
70 node.elmEvent = newModel.event;
71 var txt = newModel.text;
72 if (oldModel.text !== txt) node.innerHTML = txt;
73 }
75 function button(evnt, txt) {
76 return A3(newElement, 100, 40, {
77 ctor: 'Custom',
78 type: 'Button',
79 render: render,
80 update: update,
81 model: { event:evnt, text:JS.fromString(txt) }
82 });
83 }
85 return { _:{}, button:F2(button), events:events };
86 }
88 function customButtons(defaultValue) {
89 var events = Signal.constant(defaultValue);
91 function render(model) {
92 var btn = newNode('div');
93 btn.elmEvent = model.event;
95 btn.elmUp = Render.render(model.up);
96 btn.elmHover = Render.render(model.hover);
97 btn.elmDown = Render.render(model.down);
99 function replace(node) {
100 if (node !== btn.firstChild) btn.replaceChild(node, btn.firstChild);
101 }
102 var overCount = 0;
103 function over(e) {
104 if (overCount++ > 0) return;
105 replace(btn.elmHover);
106 }
107 function out(e) {
108 if (btn.contains(e.toElement || e.relatedTarget)) return;
109 overCount = 0;
110 replace(btn.elmUp);
111 }
112 function up() {
113 replace(btn.elmHover);
114 elm.notify(events.id, btn.elmEvent);
115 }
116 function down() { replace(btn.elmDown); }
117 btn.addEventListener('mouseover', over);
118 btn.addEventListener('mouseout' , out);
119 btn.addEventListener('mousedown', down);
120 btn.addEventListener('mouseup' , up);
122 btn.appendChild(btn.elmUp);
123 return btn;
124 }
126 function update(node, oldModel, newModel) {
127 node.elmEvent = newModel.event;
128 Render.update(node.elmUp, oldModel.up, newModel.up)
129 Render.update(node.elmHover, oldModel.hover, newModel.hover)
130 Render.update(node.elmDown, oldModel.down, newModel.down)
131 }
133 function button(evnt, up, hover, down) {
134 return A3(newElement,
135 Math.max(up.props.width, hover.props.width, down.props.width),
136 Math.max(up.props.height, hover.props.height, down.props.height),
137 { ctor: 'Custom',
138 type: 'CustomButton',
139 render: render,
140 update: update,
141 model: { event:evnt, up:up, hover:hover, down:down }
142 });
143 }
145 return { _:{}, customButton:F4(button), events:events };
146 }
149 function hoverables(defaultValue) {
150 var events = Signal.constant(defaultValue);
151 function hoverable(handler, elem) {
152 function onHover(bool) {
153 elm.notify(events.id, handler(bool));
154 }
155 var props = Utils.replace([['hover',onHover]], elem.props);
156 return { props:props, element:elem.element };
157 }
158 return { _:{}, hoverable:F2(hoverable), events:events };
159 }
162 function checkboxes(defaultValue) {
163 var events = Signal.constant(defaultValue);
165 function render(model) {
166 var b = newNode('input');
167 b.type = 'checkbox';
168 b.checked = model.checked;
169 b.style.display = 'block';
170 b.elmHandler = model.handler;
171 function change() { elm.notify(events.id, b.elmHandler(b.checked)); }
172 b.addEventListener('change', change);
173 return b;
174 }
176 function update(node, oldModel, newModel) {
177 node.elmHandler = newModel.handler;
178 node.checked = newModel.checked;
179 return true;
180 }
182 function box(handler, checked) {
183 return A3(newElement, 13, 13, {
184 ctor: 'Custom',
185 type: 'CheckBox',
186 render: render,
187 update: update,
188 model: { checked:checked, handler:handler }
189 });
190 }
192 return { _:{}, box:F2(box), events:events };
193 }
195 function setRange(node, start, end, dir) {
196 if (node.parentNode) {
197 node.setSelectionRange(start, end, dir);
198 } else {
199 setTimeout(function(){node.setSelectionRange(start, end, dir);}, 0);
200 }
201 }
203 function mkTextPool(type) { return function fields(defaultValue) {
204 var events = Signal.constant(defaultValue);
206 var state = null;
208 function render(model) {
209 var field = newNode('input');
210 field.elmHandler = model.handler;
212 field.id = 'test';
213 field.type = type;
214 field.placeholder = JS.fromString(model.placeHolder);
215 field.value = JS.fromString(model.state.string);
216 setRange(field, model.state.selectionStart, model.state.selectionEnd, 'forward');
217 field.style.border = 'none';
218 state = model.state;
220 function update() {
221 var start = field.selectionStart,
222 end = field.selectionEnd;
223 if (field.selectionDirection === 'backward') {
224 start = end;
225 end = field.selectionStart;
226 }
227 state = { _:{},
228 string:JS.toString(field.value),
229 selectionStart:start,
230 selectionEnd:end };
231 elm.notify(events.id, field.elmHandler(state));
232 }
233 function mousedown() {
234 update();
235 elm.node.addEventListener('mouseup', mouseup);
236 }
237 function mouseup() {
238 update();
239 elm.node.removeEventListener('mouseup', mouseup)
240 }
241 field.addEventListener('keyup', update);
242 field.addEventListener('mousedown', mousedown);
244 return field;
245 }
247 function update(node, oldModel, newModel) {
248 node.elmHandler = newModel.handler;
249 if (state === newModel.state) return;
250 var newStr = JS.fromString(newModel.state.string);
251 if (node.value !== newStr) node.value = newStr;
253 var start = newModel.state.selectionStart;
254 var end = newModel.state.selectionEnd;
255 var direction = 'forward';
256 if (end < start) {
257 start = end;
258 end = newModel.state.selectionStart;
259 direction = 'backward';
260 }
262 if (node.selectionStart !== start
263 || node.selectionEnd !== end
264 || node.selectionDirection !== direction) {
265 setRange(node, start, end, direction);
266 }
267 }
269 function field(handler, placeHolder, state) {
270 return A3(newElement, 200, 30,
271 { ctor: 'Custom',
272 type: type + 'Input',
273 render: render,
274 update: update,
275 model: { handler:handler,
276 placeHolder:placeHolder,
277 state:state }
278 });
279 }
281 return { _:{}, field:F3(field), events:events };
282 }
283 }
285 return elm.Native.Graphics.Input = {
286 buttons:buttons,
287 customButtons:customButtons,
288 hoverables:hoverables,
289 checkboxes:checkboxes,
290 fields:mkTextPool('text'),
291 emails:mkTextPool('email'),
292 passwords:mkTextPool('password'),
293 dropDown:dropDown
294 };
296};