From 4fd69d4547eed4efb613c7b956bcb0f3287ae2bc Mon Sep 17 00:00:00 2001 From: Przemek Kaminski <pk@intrepidus.pl> Date: Mon, 27 Jan 2020 18:52:01 +0100 Subject: [PATCH] [CodeEditor] moveUp/Down fix solved with key props Also, changed codeRef, codeTypeRef to state. --- .psc-package/local/.set/packages.json | 2 +- src/Gargantext/Components/CodeEditor.purs | 39 +++++-- src/Gargantext/Components/Nodes/Corpus.purs | 117 +++++++++++++------- 3 files changed, 109 insertions(+), 49 deletions(-) diff --git a/.psc-package/local/.set/packages.json b/.psc-package/local/.set/packages.json index ad9dee34..3cddef09 100644 --- a/.psc-package/local/.set/packages.json +++ b/.psc-package/local/.set/packages.json @@ -3336,4 +3336,4 @@ "repo": "https://github.com/paf31/purescript-yargs.git", "version": "v4.0.0" } -} +} \ No newline at end of file diff --git a/src/Gargantext/Components/CodeEditor.purs b/src/Gargantext/Components/CodeEditor.purs index e2ae3f19..53acdf8d 100644 --- a/src/Gargantext/Components/CodeEditor.purs +++ b/src/Gargantext/Components/CodeEditor.purs @@ -8,8 +8,9 @@ import Data.Generic.Rep.Show (genericShow) import Data.Maybe (Maybe(..)) import Data.Nullable (Nullable, null, toMaybe) import Data.String.Utils (endsWith) -import Data.Tuple (fst) +import Data.Tuple (fst, snd) import Data.Tuple.Nested ((/\)) +import DOM.Simple.Console (log2) import DOM.Simple.Types (Element) import Effect (Effect) import FFI.Simple ((.=), delay) @@ -98,6 +99,20 @@ codeEditorCpt = R.hooksComponent "G.C.CE.CodeEditor" cpt where cpt {code, defaultCodeType, onChange} _ = do controls <- initControls code defaultCodeType + -- codeRef <- R.useRef code + -- defaultCodeTypeRef <- R.useRef defaultCodeType + + -- R.useEffect2' code defaultCodeType $ do + -- if R.readRef codeRef == code && R.readRef defaultCodeTypeRef == defaultCodeType then + -- pure unit + -- else do + -- log2 "[codeEditorCpt] code" code + -- log2 "[codeEditorCpt] defaultCodeType" defaultCodeType + -- R.setRef codeRef code + -- R.setRef defaultCodeTypeRef defaultCodeType + -- reinitControls controls code defaultCodeType + -- setCodeOverlay controls code + -- renderHtml code controls -- Initial rendering of elements with given data @@ -127,7 +142,7 @@ codeEditorCpt = R.hooksComponent "G.C.CE.CodeEditor" cpt -- , contentEditable: "true" , ref: controls.codeOverlayElRef , rows: 30 - --, on: { input: onEditChange (fst codeType) codeElRef htmlRef editorCodeRef error } + --, on: { input: onEditChange (fst codeType) codeElRef htmlRef codeRef error } } [] ] ] @@ -158,11 +173,11 @@ codeEditorCpt = R.hooksComponent "G.C.CE.CodeEditor" cpt previewHidden _ = " hidden" onEditChange :: forall e. Record Controls -> (CodeType -> Code -> Effect Unit) -> e -> Effect Unit - onEditChange controls@{codeElRef, codeOverlayElRef, codeType: (codeType /\ _), editorCodeRef} onChange e = do + onEditChange controls@{codeElRef, codeOverlayElRef, codeType: (codeType /\ _), codeS} onChange e = do let code = R2.unsafeEventValue e - R.setRef editorCodeRef code + snd codeS $ const code setCodeOverlay controls code - renderHtml (R.readRef controls.editorCodeRef) controls + renderHtml (fst codeS) controls onChange codeType code setCodeOverlay :: Record Controls -> Code -> Effect Unit @@ -218,7 +233,7 @@ toolbarCpt = R.hooksComponent "G.C.CE.toolbar" cpt renderHtml code controls onChange (fst controls.codeType) code where - code = R.readRef controls.editorCodeRef + code = fst controls.codeS type ErrorComponentProps = @@ -310,9 +325,9 @@ viewTypeSelectorCpt = R.hooksComponent "G.C.CE.ViewTypeSelector" cpt type Controls = ( codeElRef :: R.Ref (Nullable Element) + , codeS :: R.State Code , codeType :: R.State CodeType , codeOverlayElRef :: R.Ref (Nullable Element) - , editorCodeRef :: R.Ref Code , error :: R.State (Maybe Error) , htmlElRef :: R.Ref (Nullable Element) , viewType :: R.State ViewType @@ -321,19 +336,25 @@ type Controls = initControls :: Code -> CodeType -> R.Hooks (Record Controls) initControls code defaultCodeType = do htmlElRef <- R.useRef null + codeS <- R.useState' code codeElRef <- R.useRef null codeOverlayElRef <- R.useRef null codeType <- R.useState' defaultCodeType - editorCodeRef <- R.useRef code error <- R.useState' Nothing viewType <- R.useState' Both pure $ { codeElRef + , codeS , codeType , codeOverlayElRef - , editorCodeRef , error , htmlElRef , viewType } + +reinitControls :: Record Controls -> Code -> CodeType -> Effect Unit +reinitControls c@{codeType, codeS, error} code defaultCodeType = do + snd codeType $ const defaultCodeType + snd codeS $ const code + snd error $ const Nothing diff --git a/src/Gargantext/Components/Nodes/Corpus.purs b/src/Gargantext/Components/Nodes/Corpus.purs index ce4d271c..b87d7b8f 100644 --- a/src/Gargantext/Components/Nodes/Corpus.purs +++ b/src/Gargantext/Components/Nodes/Corpus.purs @@ -6,7 +6,7 @@ import Data.Array as A import Data.Either (Either(..)) import Data.List as List import Data.Maybe (Maybe(..)) -import Data.Tuple (Tuple(..), fst) +import Data.Tuple (Tuple(..), fst, snd) import Data.Tuple.Nested ((/\)) import DOM.Simple.Console (log2) import Effect (Effect) @@ -20,7 +20,7 @@ import Gargantext.Prelude import Gargantext.Components.CodeEditor as CE import Gargantext.Components.Node (NodePoly(..), HyperdataList) -import Gargantext.Components.Nodes.Corpus.Types (CorpusData, FTField, Field(..), FieldType(..), Hash, Hyperdata(..), defaultField, defaultHaskell', defaultJSON', defaultMarkdown') +import Gargantext.Components.Nodes.Corpus.Types (CorpusData, FTField, Field(..), FieldType(..), Hyperdata(..), defaultField, defaultHaskell', defaultJSON', defaultMarkdown') import Gargantext.Data.Array as GDA import Gargantext.Hooks.Loader (useLoader) import Gargantext.Routes (SessionRoute(NodeAPI, Children)) @@ -69,6 +69,15 @@ corpusLayoutViewCpt = R.hooksComponent "G.C.N.C.corpusLayoutView" cpt cpt {corpus: (NodePoly {hyperdata: Hyperdata {fields}}), nodeId, reload, session} _ = do let fieldsWithIndex = List.mapWithIndex (\idx -> \t -> Tuple idx t) fields fieldsS <- R.useState' fieldsWithIndex + fieldsRef <- R.useRef fields + + -- handle props change of fields + R.useEffect1' fields $ do + if R.readRef fieldsRef == fields then + pure unit + else do + R.setRef fieldsRef fields + snd fieldsS $ const fieldsWithIndex pure $ H.div {} [ H.div { className: "row" } [ @@ -122,19 +131,23 @@ fieldsCodeEditorCpt :: R.Component FieldsCodeEditorProps fieldsCodeEditorCpt = R.hooksComponent "G.C.N.C.fieldsCodeEditorCpt" cpt where cpt {nodeId, fields: fS@(fields /\ _), session} _ = do - pure $ H.div {} $ List.toUnfoldable editors + masterKey <- R.useState' 0 + + pure $ H.div {} $ List.toUnfoldable (editors masterKey) where - editors = (\idxField@(Tuple idx field) -> - fieldCodeEditorWrapper { canMoveDown: idx < (List.length fields - 1) - , canMoveUp: idx > 0 - , field - , hash: hash idxField - , onChange: onChange fS idx - , onMoveDown: onMoveDown fS idx - , onMoveUp: onMoveUp fS idx - , onRemove: onRemove fS idx - , onRename: onRename fS idx - }) <$> fields + editors masterKey = + (\idxField@(Tuple idx field) -> + fieldCodeEditorWrapper { canMoveDown: idx < (List.length fields - 1) + , canMoveUp: idx > 0 + , field + , hash: hash idxField + , key: (show $ fst masterKey) <> "-" <> (show idx) + , onChange: onChange fS idx + , onMoveDown: onMoveDown masterKey fS idx + , onMoveUp: onMoveUp masterKey fS idx + , onRemove: onRemove fS idx + , onRename: onRename fS idx + }) <$> fields onChange :: R.State FTFieldsWithIndex -> Index -> FieldType -> Effect Unit onChange (_ /\ setFields) idx typ = do @@ -143,13 +156,15 @@ fieldsCodeEditorCpt = R.hooksComponent "G.C.N.C.fieldsCodeEditorCpt" cpt Nothing -> fields Just newFields -> newFields - onMoveDown :: R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit - onMoveDown (fs /\ setFields) idx _ = do + onMoveDown :: R.State Int -> R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit + onMoveDown (_ /\ setMasterKey) (fs /\ setFields) idx _ = do setFields $ recomputeIndices <<< (GDA.swapList idx (idx + 1)) + setMasterKey $ (+) 1 - onMoveUp :: R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit - onMoveUp (_ /\ setFields) idx _ = do + onMoveUp :: R.State Int -> R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit + onMoveUp (_ /\ setMasterKey) (_ /\ setFields) idx _ = do setFields $ recomputeIndices <<< (GDA.swapList idx (idx - 1)) + setMasterKey $ (+) 1 onRemove :: R.State FTFieldsWithIndex -> Index -> Unit -> Effect Unit onRemove (_ /\ setFields) idx _ = do @@ -176,7 +191,8 @@ type FieldCodeEditorProps = canMoveDown :: Boolean , canMoveUp :: Boolean , field :: FTField - , hash :: Hash + , hash :: Hash -- TODO this isn't needed anymore + , key :: String , onChange :: FieldType -> Effect Unit , onMoveDown :: Unit -> Effect Unit , onMoveUp :: Unit -> Effect Unit @@ -241,30 +257,53 @@ renameableCpt = R.hooksComponent "G.C.N.C.renameableCpt" cpt cpt {onRename, text} _ = do isEditing <- R.useState' false state <- R.useState' text + textRef <- R.useRef text + + -- handle props change of text + R.useEffect1' text $ do + if R.readRef textRef == text then + pure unit + else do + R.setRef textRef text + snd state $ const text pure $ H.div { className: "renameable" } [ - textCpt isEditing state + renameableText {isEditing, onRename, state} ] - where - textCpt :: R.State Boolean -> R.State String -> R.Element - textCpt (false /\ setIsEditing) (text /\ _) = H.div {} [ - H.span { className: "text" } [ H.text text ] - , H.span { className: "btn btn-default" - , on: { click: \_ -> setIsEditing $ const true } } [ - H.span { className: "glyphicon glyphicon-pencil" } [] - ] + +type RenameableTextProps = + ( + isEditing :: R.State Boolean + , onRename :: String -> Effect Unit + , state :: R.State String + ) + +renameableText :: Record RenameableTextProps -> R.Element +renameableText props = R.createElement renameableTextCpt props [] + +renameableTextCpt :: R.Component RenameableTextProps +renameableTextCpt = R.hooksComponent "G.C.N.C.renameableTextCpt" cpt + where + cpt {isEditing: (false /\ setIsEditing), state: (text /\ _)} _ = do + pure $ H.div {} [ + H.span { className: "text" } [ H.text text ] + , H.span { className: "btn btn-default" + , on: { click: \_ -> setIsEditing $ const true } } [ + H.span { className: "glyphicon glyphicon-pencil" } [] + ] ] - textCpt (true /\ setIsEditing) (text /\ setText) = H.div {} [ - H.input { defaultValue: text - , className: "form-control text" - , on: { change: \e -> setText $ const $ R2.unsafeEventValue e } } - , H.span { className: "btn btn-default" - , on: { click: \_ -> do - setIsEditing $ const false - onRename text - } } [ - H.span { className: "glyphicon glyphicon-floppy-disk" } [] - ] + cpt {isEditing: (true /\ setIsEditing), onRename, state: (text /\ setText)} _ = do + pure $ H.div {} [ + H.input { defaultValue: text + , className: "form-control text" + , on: { change: \e -> setText $ const $ R2.unsafeEventValue e } } + , H.span { className: "btn btn-default" + , on: { click: \_ -> do + setIsEditing $ const false + onRename text + } } [ + H.span { className: "glyphicon glyphicon-floppy-disk" } [] + ] ] fieldCodeEditor :: Record FieldCodeEditorProps -> R.Element -- 2.21.0