Get rid of Selection, simplifying the field API

Real reason is that setSelectionRange also sets the focus. This
behavior makes sense, so the underlying issue is that setting the
selection for two fields is not meaningful because only one can have
focus.

How to set focus (for fields or inputs in general) is not really
answered yet, so I think it makes the most sense to leave it out for
now.
This commit is contained in:
Evan Czaplicki 2014-03-03 00:08:58 -10:00
parent 0dcee3ec46
commit 8e2c6989b4
2 changed files with 10 additions and 96 deletions

View file

@ -6,9 +6,6 @@ approach as the [`Graphics.Input`](Graphics-Input) module for describing an
# Create Fields # Create Fields
@docs field, password, email @docs field, password, email
# Field Content
@docs Content, Selection, Direction, noContent
# Field Style # Field Style
@docs Style, Outline, noOutline, Highlight, noHighlight, Dimensions, uniformly @docs Style, Outline, noOutline, Highlight, noHighlight, Dimensions, uniformly
-} -}
@ -75,56 +72,29 @@ defaultStyle =
, style = Text.defaultStyle , style = Text.defaultStyle
} }
{-| Represents the current content of a text field. For example:
Content "She sells sea shells" (Selection 0 3 Backward)
This means the user highlighted the substring `"She"` backwards.
-}
type Content = { string:String, selection:Selection }
{-| The selection within a text field. `start` is never greater than `end`:
Selection 0 0 Forward -- cursor precedes all characters
Selection 5 9 Backward -- highlighting characters starting after
-- the 5th and ending after the 9th
-}
type Selection = { start:Int, end:Int, direction:Direction }
{-| The direction of selection.-}
data Direction = Forward | Backward
{-| A field with no content:
Content "" (Selection 0 0 Forward)
-}
noContent : Content
noContent = Content "" (Selection 0 0 Forward)
{-| Create a text field. The following example creates a time-varying element {-| Create a text field. The following example creates a time-varying element
called `nameField`. As the user types their name, the field will be updated called `nameField`. As the user types their name, the field will be updated
to match what they have entered. to match what they have entered.
name : Input Content name : Input String
name = input noContent name = input ""
nameField : Signal Element nameField : Signal Element
nameField = field name.handle id defaultStyle "Name" <~ name.signal nameField = field name.handle id defaultStyle "Name" <~ name.signal
-} -}
field : Handle a -> (Content -> a) -> Style -> String -> Content -> Element field : Handle a -> (String -> a) -> Style -> String -> String -> Element
field = Native.Graphics.Input.field field = Native.Graphics.Input.field
{-| Same as `field` but the UI element blocks out each characters. -} {-| Same as `field` but the UI element blocks out each characters. -}
password : Handle a -> (Content -> a) -> Style -> String -> Content -> Element password : Handle a -> (String -> a) -> Style -> String -> String -> Element
password = Native.Graphics.Input.password password = Native.Graphics.Input.password
{-| Same as `field` but it adds an annotation that this field is for email {-| Same as `field` but it adds an annotation that this field is for email
addresses. This is helpful for auto-complete and for mobile users who may addresses. This is helpful for auto-complete and for mobile users who may
get a custom keyboard with an `@` and `.com` button. get a custom keyboard with an `@` and `.com` button.
-} -}
email : Handle a -> (Content -> a) -> Style -> String -> Content -> Element email : Handle a -> (String -> a) -> Style -> String -> String -> Element
email = Native.Graphics.Input.email email = Native.Graphics.Input.email
-- area : Handle a -> (Content -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> Content -> Element -- area : Handle a -> (String -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> String -> Element
-- area = Native.Graphics.Input.area -- area = Native.Graphics.Input.area

View file

@ -209,14 +209,6 @@ Elm.Native.Graphics.Input.make = function(elm) {
}); });
} }
function setRange(node, start, end, dir) {
if (node.parentNode) {
node.setSelectionRange(start, end, dir);
} else {
setTimeout(function(){node.setSelectionRange(start, end, dir);}, 0);
}
}
function updateIfNeeded(css, attribute, latestAttribute) { function updateIfNeeded(css, attribute, latestAttribute) {
if (css[attribute] !== latestAttribute) { if (css[attribute] !== latestAttribute) {
css[attribute] = latestAttribute; css[attribute] = latestAttribute;
@ -267,10 +259,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
field.type = model.type; field.type = model.type;
field.placeholder = JS.fromString(model.placeHolder); field.placeholder = JS.fromString(model.placeHolder);
field.value = JS.fromString(model.content.string); field.value = JS.fromString(model.content);
var selection = model.content.selection;
var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
setRange(field, selection.start, selection.end, direction);
field.elm_signal = model.signal; field.elm_signal = model.signal;
field.elm_handler = model.handler; field.elm_handler = model.handler;
@ -282,15 +271,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
String.fromCharCode(event.keyCode) + String.fromCharCode(event.keyCode) +
curr.slice(field.selectionEnd)); curr.slice(field.selectionEnd));
var pos = field.selectionEnd + 1; var pos = field.selectionEnd + 1;
elm.notify(field.elm_signal.id, field.elm_handler({ elm.notify(field.elm_signal.id, field.elm_handler(JS.toString(next)));
_:{},
string: JS.toString(next),
selection: {
start: pos,
end: pos,
direction: { ctor:'Forward' }
},
}));
event.preventDefault(); event.preventDefault();
} }
@ -300,46 +281,12 @@ Elm.Native.Graphics.Input.make = function(elm) {
if (curr === next) { if (curr === next) {
return; return;
} }
var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
var start = field.selectionStart;
var end = field.selectionEnd;
field.value = field.elm_old_value; field.value = field.elm_old_value;
elm.notify(field.elm_signal.id, field.elm_handler(JS.toString(next)));
elm.notify(field.elm_signal.id, field.elm_handler({
_:{},
string: JS.toString(next),
selection: {
start: start,
end: end,
direction: { ctor: direction }
},
}));
} }
function mouseUpdate(event) {
var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
elm.notify(field.elm_signal.id, field.elm_handler({
_:{},
string: field.value,
selection: {
start: field.selectionStart,
end: field.selectionEnd,
direction: { ctor: direction }
},
}));
}
function mousedown(event) {
mouseUpdate(event);
elm.node.addEventListener('mouseup', mouseup);
}
function mouseup(event) {
mouseUpdate(event);
elm.node.removeEventListener('mouseup', mouseup)
}
field.addEventListener('keypress', keyUpdate); field.addEventListener('keypress', keyUpdate);
field.addEventListener('input', inputUpdate); field.addEventListener('input', inputUpdate);
field.addEventListener('mousedown', mousedown);
return field; return field;
} }
@ -353,12 +300,9 @@ Elm.Native.Graphics.Input.make = function(elm) {
field.type = newModel.type; field.type = newModel.type;
field.placeholder = JS.fromString(newModel.placeHolder); field.placeholder = JS.fromString(newModel.placeHolder);
var value = JS.fromString(newModel.content.string); var value = JS.fromString(newModel.content);
field.value = value; field.value = value;
field.elm_old_value = value; field.elm_old_value = value;
var selection = newModel.content.selection;
var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
setRange(field, selection.start, selection.end, direction);
} }
function mkField(type) { function mkField(type) {