Commit 01195bb7 authored by Bas Lijnse's avatar Bas Lijnse

Worked out composition for refined editor proposal

parent c19e0a92
Pipeline #16285 passed with stage
in 3 minutes and 51 seconds
definition module iTasks.UI.RefinedEditor definition module iTasks.UI.RefinedEditor
//TODO: Use (p r w) instead of v for external interface
//Tasks to handle side-effects just write commands to the share?
//Collaboration with a distributed message set and versions needs to be built into //Collaboration with a distributed message set and versions needs to be built into
//interact. The state and message queue will be shared between tasks instead of the editor value //interact. The state and message queue will be shared between tasks instead of the editor value
CollabModel s :== (Versioned s, [Versioned EditorUpdate]) CollabModel s :== (Versioned s, [Versioned EditorUpdate])
...@@ -7,7 +10,6 @@ Versioned a :== (!Int,!a) ...@@ -7,7 +10,6 @@ Versioned a :== (!Int,!a)
SDS Int (Either (Version s) [Version s]) SDS Int (Either (Version s) [Version s])
//Tasks to handle side-effects
//Definition of an editor //Definition of an editor
//Each editor defines a client and a server component with their own state. //Each editor defines a client and a server component with their own state.
...@@ -15,32 +17,38 @@ SDS Int (Either (Version s) [Version s]) ...@@ -15,32 +17,38 @@ SDS Int (Either (Version s) [Version s])
:: [(String, c -> (c, Maybe msg))] -> EditorClient c msg :: [(String, c -> (c, Maybe msg))] -> EditorClient c msg
:: Editor v s c = E. msg: //v = Value the editor represents (print/parse to lower level)
{ client :: EditorClient c msg //s = Raw server state, for example an unparsed string
, server :: EditorServer v s c msg //c = Client configuration, the initial setup of a client component
//m = Message type, used for communicating between client and server
:: Editor v s c m =
{ client :: EditorClient c m
, server :: EditorServer v s c m
} }
:: EditorClient c msg = :: EditorClient c m =
{ init :: c !JSObject *JSWorld -> *(!Maybe msg, !*JSWorld) { init :: c !JSObject *JSWorld -> *(!Maybe m, !*JSWorld)
, destroy :: !JSObject *JSWorld -> *JSWorld , destroy :: !JSObject *JSWorld -> *JSWorld
, onEvent :: !String !JSObject !JSObject !*JSWorld -> (!Maybe msg, !*JSWorld) //Something happened in the javascript/DOM world
, onMessage :: msg !JSObject !*JSWorld -> *(!Maybe msg, !*JSWorld) , onEvent :: !String !JSObject !JSObject !*JSWorld -> (!Maybe m, !*JSWorld)
//A message from the server arrived
, setState :: c !JSObject !*JSWorld -> *(!Maybe msg, *JSWorld) , onMessage :: m !JSObject !*JSWorld -> *(!Maybe m, !*JSWorld)
, getState :: !JSObject !*JSWorld -> *(!c, *JSWorld)
} }
:: EditorServer v s c msg = :: EditorServer v s c m =
{ init :: s -> c //Derive the initial client state from the server state { configure :: s -> c //Derive the initial client configuration
, onSet :: s s -> (s, Maybe msg) , onRefresh :: s s -> (s, Maybe m)
, onMessage :: msg s -> (s, Maybe msg) , onMessage :: m s -> (s, Maybe m)
, toValue :: s -> EditorValue v , value :: ((EditorValue v) -> s, s -> EditorValue v) //How to get to/from the validated domain
, fromValue :: (EditorValue v) -> s
} }
//Exposing editors valid/invalid/empty judgement
:: EditorValue a = ValidValue a | InvalidValue | EmptyValue
class EditorMessage m class EditorMessage m
where where
encodeEditorMessage :: m -> EditStateUpdate encodeEditorMessage :: m -> EditStateUpdate
...@@ -51,14 +59,12 @@ where ...@@ -51,14 +59,12 @@ where
encodeEditorState :: s -> EditState encodeEditorState :: s -> EditState
decodeEditorState :: EditState -> s decodeEditorState :: EditState -> s
//Exposing editors valid/invalid/empty judgement
:: EditorValue a = ValidValue a | InvalidValue | EmptyValue
//Generic untyped edit state //Generic untyped edit state
:: EditState = :: EditState =
{ attributes :: Map AttributeName AttributeValue { attributes :: Map AttributeName AttributeValue
, children :: [EditState] , children :: [EditState]
} }
:: AttributeName :== String :: AttributeName :== String
:: AttributeValue :== JSONNode :: AttributeValue :== JSONNode
...@@ -73,7 +79,6 @@ where ...@@ -73,7 +79,6 @@ where
:: ClientState :== EditState :: ClientState :== EditState
:: ServerState :== EditState :: ServerState :== EditState
//Editor composition //Editor composition
//- client side without communication //- client side without communication
//- service //- service
...@@ -84,14 +89,14 @@ where ...@@ -84,14 +89,14 @@ where
// - Dynamic label that shows the number of items // - Dynamic label that shows the number of items
// - Buttons with each item for moving i // - Buttons with each item for moving i
listEditor :: (Editor a s c) -> Editor [a] [s] [c] listEditor :: (Editor a s c m) -> Editor [a] [s] [c] (Int,m)
label :: String -> Editor String () LabelDef label :: String -> Editor String () LabelDef
button :: String -> Editor Bool Bool ButtonDef button :: (Bool -> ButtonDef) -> Editor Bool Bool ButtonDef Bool
textView :: Editor String TextViewDef TextViewDef textView :: Editor String TextViewDef TextViewDef String
textField :: Editor String TextFieldDef TextFieldDef textField :: Editor String TextFieldDef TextFieldDef String
integerField :: Editor Int TextFieldDef TextFieldDef integerField :: Editor Int TextFieldDef TextFieldDef String
:: ButtonDef = :: ButtonDef =
{ clicked :: Bool { clicked :: Bool
...@@ -109,6 +114,50 @@ integerField :: Editor Int TextFieldDef TextFieldDef ...@@ -109,6 +114,50 @@ integerField :: Editor Int TextFieldDef TextFieldDef
{ text :: String { text :: String
} }
//editor voor getal //Editor for numbers
//editor voor boolean (checkbox) //Editor for boolean (checkbox)
//combinatie voor (maybe getal) //Combination: for maybe number
//Compose by juxtaposition, no need to specify interdependency
glue :: (Editor v1 s1 c1 m1)
(Editor v2 s2 c2 m2)
->
(Editor (EditorValue v1, EditorValue v2) (s1,s2) (c1,c2) (Maybe m1, Maybe m2))
//Define the dependencies by defining feedback on messages
link ::
((c1,c2) -> (c1,c2)) //Rewrite the initial client configuration
((Maybe m1, Maybe m2) -> ((Maybe m1, Maybe m2),(Maybe m1, Maybe m2))) //Rewrite from server to client with feedback to server
((Maybe m1, Maybe m2) -> ((Maybe m1, Maybe m2),(Maybe m1, Maybe m2))) //Rewrite from client to server with feedback to client
(Editor v s (c1,c2) (Maybe m1, Maybe m2))
->
(Editor v s (c1,c2) (Maybe m1, Maybe m2))
//Get rid of the tuples and combine the parts into a unified state, configuration and
fuse ::
((c1,c2) -> c, c -> (c1,c2)) //Unify client configuration
((s1,s2) -> s, s -> (s1,s2)) //Unify server state
(m -> (Maybe m1, Maybe m2), (Maybe m1, Maybe m2) -> Maybe m) //Unify messages
(EditorValue v -> (EditorValue v1, EditorValue v2), (EditorValue v1,EditorValue v2) -> EditorValue v) //Unity checked interface
(Editor (EditorValue v1, EditorValue v2) (s1,s2) (c1,c2) (Maybe m1, Maybe m2))
->
(Editor v s c m)
//Example : Texfield with reset button
resettableField = fuseFst buttonconfig (link id serverlink clientlink (glue textField (button (const buttonconfig))))
where
clientlink (_,Just True) = ((Just "",Nothing), (Just "",Just False)) //When button is clicked, reset both local and remote
clientlink (mt,mb) = ((mt,mb),(Nothing,Nothing)
serverlink m = (m,(Nothing,Nothing)) //No feedback serverside
buttonconfig = {ButtonDef|clicked=False,label=Just "Reset",icon=Nothing}
//Common pattern: expose first component
fuseFst sndCfg editor = fuse clientconfig serverstate messages interface
where
clientconfig = (fst, \s -> (s,sndCfg)) //Reset button is statically configured
serverstate = (fst, \s -> (s,False)) //Only server state of the textfield is stored
messages = (\m -> (Just m,Nothing), fst) //Drop messages for second part of the tuple
interface = (x -> (x,EmptyValue), fst) //Only consider first part as editor
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment