on 6325b7e help sign in
379 lines 13 kB history raw
1Elm.Native.Graphics.Input = {};
2Elm.Native.Graphics.Input.make = function(elm) {
3 elm.Native = elm.Native || {};
4 elm.Native.Graphics = elm.Native.Graphics || {};
5 elm.Native.Graphics.Input = elm.Native.Graphics.Input || {};
6 if (elm.Native.Graphics.Input.values) return elm.Native.Graphics.Input.values;
8 var Render = ElmRuntime.use(ElmRuntime.Render.Element);
9 var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
11 var toCss = Elm.Native.Color.make(elm).toCss;
12 var Text = Elm.Native.Text.make(elm);
13 var Signal = Elm.Signal.make(elm);
14 var newElement = Elm.Graphics.Element.make(elm).newElement;
15 var List = Elm.Native.List.make(elm);
16 var Utils = Elm.Native.Utils.make(elm);
17 var Tuple2 = Utils.Tuple2;
19 function input(initialValue) {
20 var signal = Signal.constant(initialValue);
21 return { _:{}, signal:signal, handle:signal };
22 }
24 function renderDropDown(signal, values) {
25 return function(_) {
26 var entries = List.toArray(values);
28 var drop = newNode('select');
29 drop.style.border = '0 solid';
30 drop.style.pointerEvents = 'auto';
31 drop.style.display = 'block';
32 for (var i = 0; i < entries.length; ++i) {
33 var option = newNode('option');
34 var name = entries[i]._0;
35 option.value = name;
36 option.innerHTML = name;
37 drop.appendChild(option);
38 }
39 drop.addEventListener('change', function() {
40 elm.notify(signal.id, entries[drop.selectedIndex]._1);
41 });
43 var t = drop.cloneNode(true);
44 t.style.visibility = "hidden";
46 elm.node.appendChild(t);
47 var style = window.getComputedStyle(t, null);
48 var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
49 var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
50 elm.node.removeChild(t);
51 return drop;
52 };
53 }
55 function updateDropDown(node, oldModel, newModel) {
56 }
58 function dropDown(signal, values) {
59 return A3(newElement, 100, 24, {
60 ctor: 'Custom',
61 type: 'DropDown',
62 render: renderDropDown(signal,values),
63 update: updateDropDown,
64 model: {}
65 });
66 }
68 function renderButton(model) {
69 var node = newNode('button');
70 node.style.display = 'block';
71 node.style.pointerEvents = 'auto';
72 node.elm_signal = model.signal;
73 node.elm_value = model.value;
74 function click() {
75 elm.notify(node.elm_signal.id, node.elm_value);
76 }
77 node.addEventListener('click', click);
78 node.innerHTML = model.text;
79 return node;
80 }
82 function updateButton(node, oldModel, newModel) {
83 node.elm_signal = newModel.signal;
84 node.elm_value = newModel.value;
85 var txt = newModel.text;
86 if (oldModel.text !== txt) node.innerHTML = txt;
87 }
89 function button(signal, value, text) {
90 return A3(newElement, 100, 40, {
91 ctor: 'Custom',
92 type: 'Button',
93 render: renderButton,
94 update: updateButton,
95 model: { signal:signal, value:value, text:text }
96 });
97 }
99 function renderCustomButton(model) {
100 var btn = newNode('div');
101 btn.style.pointerEvents = 'auto';
102 btn.elm_signal = model.signal;
103 btn.elm_value = model.value;
105 btn.elm_up = Render.render(model.up);
106 btn.elm_hover = Render.render(model.hover);
107 btn.elm_down = Render.render(model.down);
109 function replace(node) {
110 if (node !== btn.firstChild) {
111 btn.replaceChild(node, btn.firstChild);
112 }
113 }
114 var overCount = 0;
115 function over(e) {
116 if (overCount++ > 0) return;
117 replace(btn.elm_hover);
118 }
119 function out(e) {
120 if (btn.contains(e.toElement || e.relatedTarget)) return;
121 overCount = 0;
122 replace(btn.elm_up);
123 }
124 function up() {
125 replace(btn.elm_hover);
126 elm.notify(btn.elm_signal.id, btn.elm_value);
127 }
128 function down() {
129 replace(btn.elm_down);
130 }
131 btn.addEventListener('mouseover', over);
132 btn.addEventListener('mouseout' , out);
133 btn.addEventListener('mousedown', down);
134 btn.addEventListener('mouseup' , up);
136 btn.appendChild(btn.elm_up);
138 return btn;
139 }
141 function updateCustomButton(node, oldModel, newModel) {
142 var signal = newModel.signal;
143 node.elm_up.elm_signal = signal;
144 node.elm_hover.elm_signal = signal;
145 node.elm_down.elm_signal = signal;
147 var value = newModel.value;
148 node.elm_up.elm_value = value;
149 node.elm_hover.elm_value = value;
150 node.elm_down.elm_value = value;
152 Render.update(node.elm_up, oldModel.up, newModel.up)
153 Render.update(node.elm_hover, oldModel.hover, newModel.hover)
154 Render.update(node.elm_down, oldModel.down, newModel.down)
155 }
157 function max3(a,b,c) {
158 var ab = a > b ? a : b;
159 return ab > c ? ab : c;
160 }
162 function customButton(signal, value, up, hover, down) {
163 return A3(newElement,
164 max3(up.props.width, hover.props.width, down.props.width),
165 max3(up.props.height, hover.props.height, down.props.height),
166 { ctor: 'Custom',
167 type: 'CustomButton',
168 render: renderCustomButton,
169 update: updateCustomButton,
170 model: { signal:signal, value:value, up:up, hover:hover, down:down }
171 });
172 }
174 function renderCheckbox(model) {
175 var node = newNode('input');
176 node.type = 'checkbox';
177 node.checked = model.checked;
178 node.style.display = 'block';
179 node.style.pointerEvents = 'auto';
180 node.elm_signal = model.signal;
181 node.elm_handler = model.handler;
182 function change() {
183 elm.notify(node.elm_signal.id, node.elm_handler(node.checked));
184 }
185 node.addEventListener('change', change);
186 return node;
187 }
189 function updateCheckbox(node, oldModel, newModel) {
190 node.elm_signal = newModel.signal;
191 node.elm_handler = newModel.handler;
192 node.checked = newModel.checked;
193 return true;
194 }
196 function checkbox(signal, handler, checked) {
197 return A3(newElement, 13, 13, {
198 ctor: 'Custom',
199 type: 'CheckBox',
200 render: renderCheckbox,
201 update: updateCheckbox,
202 model: { signal:signal, handler:handler, checked:checked }
203 });
204 }
206 function setRange(node, start, end, dir) {
207 if (node.parentNode) {
208 node.setSelectionRange(start, end, dir);
209 } else {
210 setTimeout(function(){node.setSelectionRange(start, end, dir);}, 0);
211 }
212 }
214 function updateIfNeeded(css, attribute, latestAttribute) {
215 if (css[attribute] !== latestAttribute) {
216 css[attribute] = latestAttribute;
217 }
218 }
219 function cssDimensions(dimensions) {
220 return dimensions.top + 'px ' +
221 dimensions.right + 'px ' +
222 dimensions.bottom + 'px ' +
223 dimensions.left + 'px';
224 }
225 function updateFieldStyle(css, style) {
226 updateIfNeeded(css, 'padding', cssDimensions(style.padding));
228 var outline = style.outline;
229 updateIfNeeded(css, 'border-width', cssDimensions(outline.width));
230 updateIfNeeded(css, 'border-color', toCss(outline.color));
231 updateIfNeeded(css, 'border-radius', outline.radius + 'px');
233 var highlight = style.highlight;
234 if (highlight.width === 0) {
235 css.outline = 'none';
236 } else {
237 updateIfNeeded(css, 'outline-width', highlight.width + 'px');
238 updateIfNeeded(css, 'outline-color', toCss(highlight.color));
239 }
241 var textStyle = style.style;
242 updateIfNeeded(css, 'color', toCss(textStyle.color));
243 if (textStyle.typeface.ctor !== '[]') {
244 updateIfNeeded(css, 'font-family', Text.toTypefaces(textStyle.typeface));
245 }
246 if (textStyle.height.ctor !== "Nothing") {
247 updateIfNeeded(css, 'font-size', textStyle.height._0 + 'px');
248 }
249 updateIfNeeded(css, 'font-weight', textStyle.bold ? 'bold' : 'normal');
250 updateIfNeeded(css, 'font-style', textStyle.italic ? 'italic' : 'normal');
251 if (textStyle.line.ctor !== 'Nothing') {
252 updateIfNeeded(css, 'text-decoration', Text.toLine(textStyle.line._0));
253 }
254 }
256 function renderField(model) {
257 var field = newNode('input');
258 updateFieldStyle(field.style, model.style);
259 field.style.borderStyle = 'solid';
260 field.style.pointerEvents = 'auto';
262 field.type = model.type;
263 field.placeholder = model.placeHolder;
264 field.value = model.content.string;
266 field.elm_signal = model.signal;
267 field.elm_handler = model.handler;
268 field.elm_old_value = field.value;
270 function inputUpdate(event) {
271 var curr = field.elm_old_value;
272 var next = field.value;
273 if (curr === next) {
274 return;
275 }
277 var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
278 var start = field.selectionStart;
279 var end = field.selectionEnd;
280 field.value = field.elm_old_value;
282 elm.notify(field.elm_signal.id, field.elm_handler({
283 _:{},
284 string: next,
285 selection: {
286 _:{},
287 start: start,
288 end: end,
289 direction: { ctor: direction }
290 }
291 }));
292 }
294 field.addEventListener('input', inputUpdate);
295 field.addEventListener('focus', function() {
296 field.elm_hasFocus = true;
297 });
298 field.addEventListener('blur', function() {
299 field.elm_hasFocus = false;
300 });
302 return field;
303 }
305 function updateField(field, oldModel, newModel) {
306 if (oldModel.style !== newModel.style) {
307 updateFieldStyle(field.style, newModel.style);
308 }
309 field.elm_signal = newModel.signal;
310 field.elm_handler = newModel.handler;
312 field.type = newModel.type;
313 field.placeholder = newModel.placeHolder;
314 var value = newModel.content.string;
315 field.value = value;
316 field.elm_old_value = value;
317 if (field.elm_hasFocus) {
318 var selection = newModel.content.selection;
319 var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
320 setRange(field, selection.start, selection.end, direction);
321 }
322 }
324 function mkField(type) {
325 function field(style, signal, handler, placeHolder, content) {
326 var padding = style.padding;
327 var outline = style.outline.width;
328 var adjustWidth = padding.left + padding.right + outline.left + outline.right;
329 var adjustHeight = padding.top + padding.bottom + outline.top + outline.bottom;
330 return A3(newElement, 200, 30, {
331 ctor: 'Custom',
332 type: type + 'Field',
333 adjustWidth: adjustWidth,
334 adjustHeight: adjustHeight,
335 render: renderField,
336 update: updateField,
337 model: {
338 signal:signal,
339 handler:handler,
340 placeHolder:placeHolder,
341 content:content,
342 style:style,
343 type:type
344 }
345 });
346 }
347 return F5(field);
348 }
350 function hoverable(signal, handler, elem) {
351 function onHover(bool) {
352 elm.notify(signal.id, handler(bool));
353 }
354 var props = Utils.replace([['hover',onHover]], elem.props);
355 return { props:props, element:elem.element };
356 }
358 function clickable(signal, value, elem) {
359 function onClick(bool) {
360 elm.notify(signal.id, value);
361 }
362 var props = Utils.replace([['click',onClick]], elem.props);
363 return { props:props, element:elem.element };
364 }
366 return elm.Native.Graphics.Input.values = {
367 input:input,
368 button:F3(button),
369 customButton:F5(customButton),
370 checkbox:F3(checkbox),
371 dropDown:F2(dropDown),
372 field:mkField('text'),
373 email:mkField('email'),
374 password:mkField('password'),
375 hoverable:F3(hoverable),
376 clickable:F3(clickable)
377 };
379};