From ab7896b098c04c48fb5a8a3cd2956312166a7cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Knuchel?= Date: Mon, 11 Dec 2023 09:32:20 +0100 Subject: [PATCH] Undo for show/hide columns --- frontend/src/Libs/Maybe.elm | 12 +++++- frontend/src/Models/Project/ColumnPath.elm | 2 +- .../Organization_/Project_/Models.elm | 2 +- .../Project_/Models/ErdColumnProps.elm | 43 +++++++++++++++---- .../Organization_/Project_/Updates.elm | 10 ++--- .../Organization_/Project_/Updates/Hotkey.elm | 2 +- .../Organization_/Project_/Updates/Table.elm | 30 +++++++------ .../Organization_/Project_/Views.elm | 2 +- .../Views/Modals/ColumnContextMenu.elm | 2 +- frontend/src/Services/Lenses.elm | 27 ++++++++++++ .../Project_/Models/ErdColumnPropsTest.elm | 40 +++++++++++++++++ 11 files changed, 140 insertions(+), 32 deletions(-) diff --git a/frontend/src/Libs/Maybe.elm b/frontend/src/Libs/Maybe.elm index 21e0da24e..3fbe4407a 100644 --- a/frontend/src/Libs/Maybe.elm +++ b/frontend/src/Libs/Maybe.elm @@ -1,4 +1,4 @@ -module Libs.Maybe exposing (all, andThenZip, any, any2, filter, filterBy, filterNot, has, hasBy, isJust, mapOrElse, merge, onNothing, orElse, resultSeq, toList, toResult, toResultErr, when, zip, zip3) +module Libs.Maybe exposing (all, andThenZip, any, any2, filter, filterBy, filterNot, flipWith, has, hasBy, isJust, mapOrElse, merge, onNothing, orElse, resultSeq, toList, toResult, toResultErr, when, zip, zip3) import Libs.Bool as B @@ -102,6 +102,16 @@ andThenZip f maybe = maybe |> Maybe.andThen (\a -> f a |> Maybe.map (\b -> ( a, b ))) +flipWith : b -> Maybe a -> Maybe b +flipWith b m = + case m of + Just _ -> + Nothing + + Nothing -> + Just b + + fold : b -> (a -> b) -> Maybe a -> b fold empty transform maybe = case maybe of diff --git a/frontend/src/Models/Project/ColumnPath.elm b/frontend/src/Models/Project/ColumnPath.elm index 66a7edb80..f19dec68f 100644 --- a/frontend/src/Models/Project/ColumnPath.elm +++ b/frontend/src/Models/Project/ColumnPath.elm @@ -1,4 +1,4 @@ -module Models.Project.ColumnPath exposing (ColumnPath, ColumnPathStr, child, decode, decodeStr, encode, encodeStr, fromString, get, isRoot, merge, name, parent, rootName, show, startsWith, toString, update, withName) +module Models.Project.ColumnPath exposing (ColumnPath, ColumnPathStr, child, decode, decodeStr, encode, encodeStr, fromString, get, isRoot, merge, name, parent, rootName, separator, show, startsWith, toString, update, withName) import Dict exposing (Dict) import Json.Decode as Decode exposing (Decoder, Value) diff --git a/frontend/src/PagesComponents/Organization_/Project_/Models.elm b/frontend/src/PagesComponents/Organization_/Project_/Models.elm index 29117b8c7..c5934cb62 100644 --- a/frontend/src/PagesComponents/Organization_/Project_/Models.elm +++ b/frontend/src/PagesComponents/Organization_/Project_/Models.elm @@ -242,7 +242,7 @@ type Msg | ShowRelatedTables TableId | HideRelatedTables TableId | ToggleTableCollapse TableId - | ShowColumn ColumnRef + | ShowColumn Int ColumnRef | HideColumn ColumnRef | ShowColumns TableId ShowColumns | HideColumns TableId HideColumns diff --git a/frontend/src/PagesComponents/Organization_/Project_/Models/ErdColumnProps.elm b/frontend/src/PagesComponents/Organization_/Project_/Models/ErdColumnProps.elm index d28ae77a2..342cd8eb1 100644 --- a/frontend/src/PagesComponents/Organization_/Project_/Models/ErdColumnProps.elm +++ b/frontend/src/PagesComponents/Organization_/Project_/Models/ErdColumnProps.elm @@ -1,4 +1,4 @@ -module PagesComponents.Organization_.Project_.Models.ErdColumnProps exposing (ErdColumnProps, ErdColumnPropsFlat, ErdColumnPropsNested(..), add, children, createAll, createChildren, filter, find, flatten, getIndex, initAll, map, mapAll, mapAt, member, nest, remove, unpackAll) +module PagesComponents.Organization_.Project_.Models.ErdColumnProps exposing (ErdColumnProps, ErdColumnPropsFlat, ErdColumnPropsNested(..), children, createAll, createChildren, filter, find, flatten, getIndex, initAll, insertAt, map, mapAll, mapAt, member, nest, remove, removeWithIndex, unpackAll) import Dict import Libs.List as List @@ -134,14 +134,34 @@ remove path columns = ) -add : ColumnPath -> List ErdColumnProps -> List ErdColumnProps -add path columns = +removeWithIndex : ColumnPath -> List ErdColumnProps -> ( List ErdColumnProps, Maybe Int ) +removeWithIndex path columns = + columns + |> List.zipWithIndex + |> List.map + (\( c, i ) -> + if c.name == path.head then + path.tail |> Nel.fromList |> Maybe.map (\p -> c |> mapChildrenT (removeWithIndex p)) |> Maybe.withDefault ( c, Just i ) + + else + ( c, Nothing ) + ) + |> (\result -> + -- remove prop if found at depth of path + ( result |> List.filterMap (\( c, found ) -> found |> Maybe.filter (\_ -> path.tail |> List.isEmpty) |> Maybe.flipWith c) + , result |> List.filterMap Tuple.second |> List.head + ) + ) + + +insertAt : Int -> ColumnPath -> List ErdColumnProps -> List ErdColumnProps +insertAt index path columns = if columns |> List.memberBy .name path.head then columns |> List.map (\c -> if c.name == path.head then - path.tail |> Nel.fromList |> Maybe.mapOrElse (\p -> c |> mapChildren (add p)) c + path.tail |> Nel.fromList |> Maybe.mapOrElse (\p -> c |> mapChildren (insertAt index p)) c else c @@ -149,11 +169,11 @@ add path columns = else columns - ++ [ { name = path.head - , children = ErdColumnPropsNested (path.tail |> Nel.fromList |> Maybe.mapOrElse (\p -> [] |> add p) []) - , highlighted = False - } - ] + |> List.insertAt index + { name = path.head + , children = ErdColumnPropsNested (path.tail |> Nel.fromList |> Maybe.mapOrElse (\p -> [] |> insertAt index p) []) + , highlighted = False + } map : (ColumnPath -> ErdColumnProps -> ErdColumnProps) -> List ErdColumnProps -> List ErdColumnProps @@ -201,3 +221,8 @@ children column = mapChildren : (List ErdColumnProps -> List ErdColumnProps) -> ErdColumnProps -> ErdColumnProps mapChildren f column = { column | children = column |> children |> f |> ErdColumnPropsNested } + + +mapChildrenT : (List ErdColumnProps -> ( List ErdColumnProps, a )) -> ErdColumnProps -> ( ErdColumnProps, a ) +mapChildrenT f column = + column |> children |> f |> (\( cols, a ) -> ( { column | children = cols |> ErdColumnPropsNested }, a )) diff --git a/frontend/src/PagesComponents/Organization_/Project_/Updates.elm b/frontend/src/PagesComponents/Organization_/Project_/Updates.elm index 16aa7e811..797287562 100644 --- a/frontend/src/PagesComponents/Organization_/Project_/Updates.elm +++ b/frontend/src/PagesComponents/Organization_/Project_/Updates.elm @@ -159,11 +159,11 @@ update doCmd urlLayout zone now urlInfos organizations projects msg model = in model |> mapErdMCmd (\erd -> erd |> Erd.mapCurrentLayoutWithTimeCmd now (mapTablesCmd (mapTablePropOrSelected erd.settings.defaultSchema id (mapProps (setCollapsed (not collapsed)))))) |> setDirtyCmd - ShowColumn { table, column } -> - model |> mapErdM (showColumn now table column) |> setDirty + ShowColumn index column -> + model |> mapErdMTM (showColumn now index column) |> addHistoryT doCmd |> setDirty - HideColumn { table, column } -> - model |> mapErdM (hideColumn now table column) |> hoverNextColumn table column |> setDirty + HideColumn column -> + model |> mapErdMTM (hideColumn now column) |> addHistoryT doCmd |> hoverNextColumn column |> setDirty ShowColumns id kind -> model |> mapErdMCmd (showColumns now id kind) |> setDirtyCmd @@ -588,7 +588,7 @@ handleJsMessage now urlLayout msg model = ( model, T.send (TableColor id color) ) GotColumnShow ref -> - ( model, T.send (ShowColumn ref) ) + ( model, T.send (ShowColumn 1000 ref) ) GotColumnHide ref -> ( model, T.send (HideColumn ref) ) diff --git a/frontend/src/PagesComponents/Organization_/Project_/Updates/Hotkey.elm b/frontend/src/PagesComponents/Organization_/Project_/Updates/Hotkey.elm index 9641ede0a..fd2f1048a 100644 --- a/frontend/src/PagesComponents/Organization_/Project_/Updates/Hotkey.elm +++ b/frontend/src/PagesComponents/Organization_/Project_/Updates/Hotkey.elm @@ -178,7 +178,7 @@ shrinkElement model = showElement : Model -> Cmd Msg showElement model = (model |> currentColumnRow |> Maybe.map (\( id, col ) -> TableRow.ShowColumn (ColumnPath.toString col) |> TableRowMsg id |> T.send)) - |> Maybe.orElse (model |> currentColumn |> Maybe.map (ShowColumn >> T.send)) + |> Maybe.orElse (model |> currentColumn |> Maybe.map (ShowColumn 1000 >> T.send)) |> Maybe.orElse (model |> currentTable |> Maybe.map (\t -> ShowTable t Nothing "hotkey" |> T.send)) |> Maybe.withDefault ("Can't find an element to show :(" |> Toasts.info |> Toast |> T.send) diff --git a/frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm b/frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm index 8f372b81f..9744418d5 100644 --- a/frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm +++ b/frontend/src/PagesComponents/Organization_/Project_/Updates/Table.elm @@ -37,7 +37,7 @@ import PagesComponents.Organization_.Project_.Models.MemoId as MemoId import PagesComponents.Organization_.Project_.Models.PositionHint as PositionHint exposing (PositionHint(..)) import PagesComponents.Organization_.Project_.Models.ShowColumns as ShowColumns exposing (ShowColumns) import Ports -import Services.Lenses exposing (mapCanvas, mapColumns, mapMemos, mapProps, mapRelatedTables, mapTableRows, mapTables, mapTablesL, setHighlighted, setHoverColumn, setPosition, setSelected, setShown) +import Services.Lenses exposing (mapCanvas, mapColumns, mapColumnsT, mapMemos, mapProps, mapRelatedTables, mapTableRows, mapTables, mapTablesL, mapTablesLTM, setHighlighted, setHoverColumn, setPosition, setSelected, setShown) import Services.Toasts as Toasts import Set exposing (Set) import Time @@ -271,26 +271,32 @@ hideRelatedTables now id erd = ) -showColumn : Time.Posix -> TableId -> ColumnPath -> Erd -> Erd -showColumn now table column erd = - erd |> Erd.mapCurrentLayoutWithTime now (mapTablesL .id table (mapColumns (ErdColumnProps.remove column >> ErdColumnProps.add column))) +showColumn : Time.Posix -> Int -> ColumnRef -> Erd -> ( Erd, Maybe ( Msg, Msg ) ) +showColumn now index column erd = + ( erd |> Erd.mapCurrentLayoutWithTime now (mapTablesL .id column.table (mapColumns (ErdColumnProps.remove column.column >> ErdColumnProps.insertAt index column.column))) + , Just ( HideColumn column, ShowColumn index column ) + ) -hideColumn : Time.Posix -> TableId -> ColumnPath -> Erd -> Erd -hideColumn now table column erd = - erd |> Erd.mapCurrentLayoutWithTime now (mapTablesL .id table (mapColumns (ErdColumnProps.remove column))) +hideColumn : Time.Posix -> ColumnRef -> Erd -> ( Erd, Maybe ( Msg, Msg ) ) +hideColumn now column erd = + erd + |> Erd.mapCurrentLayoutTMWithTime now + (mapTablesLTM .id column.table (mapColumnsT (ErdColumnProps.removeWithIndex column.column)) + >> Tuple.mapSecond (Maybe.map (\i -> ( ShowColumn i column, HideColumn column ))) + ) -hoverNextColumn : TableId -> ColumnPath -> Model -> Model -hoverNextColumn table column model = +hoverNextColumn : ColumnRef -> Model -> Model +hoverNextColumn column model = let nextColumn : Maybe ColumnPath nextColumn = model.erd - |> Maybe.andThen (Erd.currentLayout >> .tables >> List.findBy .id table) - |> Maybe.andThen (.columns >> ErdColumnProps.unpackAll >> List.dropUntil (\p -> p == column) >> List.drop 1 >> List.head) + |> Maybe.andThen (Erd.currentLayout >> .tables >> List.findBy .id column.table) + |> Maybe.andThen (.columns >> ErdColumnProps.unpackAll >> List.dropUntil (\p -> p == column.column) >> List.drop 1 >> List.head) in - model |> setHoverColumn (nextColumn |> Maybe.map (ColumnRef table)) + model |> setHoverColumn (nextColumn |> Maybe.map (ColumnRef column.table)) showColumns : Time.Posix -> TableId -> ShowColumns -> Erd -> ( Erd, Cmd msg ) diff --git a/frontend/src/PagesComponents/Organization_/Project_/Views.elm b/frontend/src/PagesComponents/Organization_/Project_/Views.elm index 1a5de80df..53d61ae7a 100644 --- a/frontend/src/PagesComponents/Organization_/Project_/Views.elm +++ b/frontend/src/PagesComponents/Organization_/Project_/Views.elm @@ -136,7 +136,7 @@ viewLeftSidebar model = let content : Maybe (Html Msg) content = - model.detailsSidebar |> Maybe.map2 (DetailsSidebar.view DetailsSidebarMsg (\id -> ShowTable id Nothing "details") ShowColumn HideColumn (LLoad >> LayoutMsg) (\source q -> DataExplorer.Open (Just source) (Just q) |> DataExplorerMsg) model.tableStats model.columnStats) model.erd + model.detailsSidebar |> Maybe.map2 (DetailsSidebar.view DetailsSidebarMsg (\id -> ShowTable id Nothing "details") (ShowColumn 1000) HideColumn (LLoad >> LayoutMsg) (\source q -> DataExplorer.Open (Just source) (Just q) |> DataExplorerMsg) model.tableStats model.columnStats) model.erd in aside [ css [ "block flex-shrink-0 order-first" ] ] [ div [ css [ B.cond (content == Nothing) "-ml-112" "", "w-112 transition-[margin] ease-in-out duration-200 h-full relative flex flex-col border-r border-gray-200 bg-white overflow-y-auto" ] ] diff --git a/frontend/src/PagesComponents/Organization_/Project_/Views/Modals/ColumnContextMenu.elm b/frontend/src/PagesComponents/Organization_/Project_/Views/Modals/ColumnContextMenu.elm index ad64e3b80..a545f28a1 100644 --- a/frontend/src/PagesComponents/Organization_/Project_/Views/Modals/ColumnContextMenu.elm +++ b/frontend/src/PagesComponents/Organization_/Project_/Views/Modals/ColumnContextMenu.elm @@ -42,7 +42,7 @@ view platform index ref column notes = viewHidden : Platform -> Int -> ColumnRef -> Maybe ErdColumn -> Maybe Notes -> Html Msg viewHidden platform _ column erdColumn notes = div [] - [ ContextMenu.btnHotkey "" (ShowColumn column) [] [ text "Show column" ] platform (Conf.hotkeys |> Dict.getOrElse "show" []) + [ ContextMenu.btnHotkey "" (ShowColumn 1000 column) [] [ text "Show column" ] platform (Conf.hotkeys |> Dict.getOrElse "show" []) , ContextMenu.btn "" (DetailsSidebarMsg (DetailsSidebar.ShowColumn column)) [] [ text "Show details" ] , erdColumn |> Maybe.andThen (\c -> c.origins |> List.findMap (\o -> o.db |> Maybe.map (\url -> ( o.id, url )))) diff --git a/frontend/src/Services/Lenses.elm b/frontend/src/Services/Lenses.elm index 82c31153f..aef979131 100644 --- a/frontend/src/Services/Lenses.elm +++ b/frontend/src/Services/Lenses.elm @@ -8,6 +8,7 @@ module Services.Lenses exposing , mapCollapseTableColumns , mapColumnBasicTypes , mapColumns + , mapColumnsT , mapContent , mapContextMenuM , mapDataExplorerCmd @@ -90,6 +91,7 @@ module Services.Lenses exposing , mapTables , mapTablesCmd , mapTablesL + , mapTablesLTM , mapToasts , mapToastsCmd , mapTokenFormM @@ -323,6 +325,11 @@ mapColumns = map_ .columns setColumns +mapColumnsT : (v -> ( v, a )) -> { item | columns : v } -> ( { item | columns : v }, a ) +mapColumnsT = + mapT_ .columns setColumns + + setColumnBasicTypes : v -> { item | columnBasicTypes : v } -> { item | columnBasicTypes : v } setColumnBasicTypes = set_ .columnBasicTypes (\value item -> { item | columnBasicTypes = value }) @@ -1203,6 +1210,11 @@ mapTablesL = mapL_ .tables setTables +mapTablesLTM : (v -> k) -> k -> (v -> ( v, Maybe a )) -> { item | tables : List v } -> ( { item | tables : List v }, Maybe a ) +mapTablesLTM = + mapLTM_ .tables setTables + + mapTablesCmd : (v -> ( v, Cmd msg )) -> { item | tables : v } -> ( { item | tables : v }, Cmd msg ) mapTablesCmd = mapT_ .tables setTables @@ -1431,6 +1443,21 @@ mapL_ get update getKey key transform item = item +mapLTM_ : (item -> List v) -> (List v -> item -> item) -> (v -> k) -> k -> (v -> ( v, Maybe a )) -> item -> ( item, Maybe a ) +mapLTM_ get update getKey key transform item = + item + |> get + |> List.map + (\v -> + if getKey v == key then + transform v + + else + ( v, Nothing ) + ) + |> (\res -> ( update (res |> List.map Tuple.first) item, res |> List.filterMap Tuple.second |> List.head )) + + --pure : a -> ( a, Cmd msg ) --pure a = diff --git a/frontend/tests/PagesComponents/Organization_/Project_/Models/ErdColumnPropsTest.elm b/frontend/tests/PagesComponents/Organization_/Project_/Models/ErdColumnPropsTest.elm index 4885a5b06..73455f396 100644 --- a/frontend/tests/PagesComponents/Organization_/Project_/Models/ErdColumnPropsTest.elm +++ b/frontend/tests/PagesComponents/Organization_/Project_/Models/ErdColumnPropsTest.elm @@ -1,6 +1,7 @@ module PagesComponents.Organization_.Project_.Models.ErdColumnPropsTest exposing (..) import Expect +import Models.Project.ColumnPath as ColumnPath exposing (ColumnPath) import PagesComponents.Organization_.Project_.Models.ErdColumnProps as ErdColumnProps exposing (ErdColumnProps, ErdColumnPropsNested(..)) import Test exposing (Test, describe, test) @@ -9,6 +10,32 @@ suite : Test suite = describe "PagesComponents.Organization_.Project_.Models.ErdColumnProps" [ test "flat and nest" (\_ -> props |> ErdColumnProps.flatten |> ErdColumnProps.nest |> Expect.equal props) + , describe "removeWithIndex" + [ test "not found" + (\_ -> + [ prop "id" [], prop "name" [] ] + |> ErdColumnProps.removeWithIndex (path "slug") + |> Expect.equal ( [ prop "id" [], prop "name" [] ], Nothing ) + ) + , test "found at root" + (\_ -> + [ prop "id" [], prop "name" [] ] + |> ErdColumnProps.removeWithIndex (path "name") + |> Expect.equal ( [ prop "id" [] ], Just 1 ) + ) + , test "found nested" + (\_ -> + [ prop "id" [], prop "name" [ prop "first" [], prop "last" [] ] ] + |> ErdColumnProps.removeWithIndex (path "name.first") + |> Expect.equal ( [ prop "id" [], prop "name" [ prop "last" [] ] ], Just 0 ) + ) + , test "not found nested" + (\_ -> + [ prop "id" [], prop "name" [ prop "first" [], prop "last" [] ] ] + |> ErdColumnProps.removeWithIndex (path "name.middle") + |> Expect.equal ( [ prop "id" [], prop "name" [ prop "first" [], prop "last" [] ] ], Nothing ) + ) + ] ] @@ -23,3 +50,16 @@ buildProps name children highlighted = , children = ErdColumnPropsNested children , highlighted = highlighted } + + +prop : String -> List ErdColumnProps -> ErdColumnProps +prop name children = + { name = name + , children = ErdColumnPropsNested children + , highlighted = False + } + + +path : String -> ColumnPath +path value = + value |> String.replace "." ColumnPath.separator |> ColumnPath.fromString