From 1da9ac1ea5d4914b20e5fdc8fa244ad93e714932 Mon Sep 17 00:00:00 2001 From: davidvader Date: Wed, 24 Jan 2024 18:06:34 -0600 Subject: [PATCH] refactor: fix line focus dom functionality --- src/elm/Components/Logs.elm | 2 +- src/elm/Pages/Org_/Repo_/Build_.elm | 48 ++++++-- src/elm/Pages/Org_/Repo_/Build_/Graph.elm | 4 +- src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm | 58 +++++---- src/elm/Pages/Org_/Repo_/Build_/Services.elm | 54 ++++++--- src/elm/Utils/Focus.elm | 120 ++++++++++++++++--- 6 files changed, 208 insertions(+), 78 deletions(-) diff --git a/src/elm/Components/Logs.elm b/src/elm/Components/Logs.elm index 5cadaab5b..df20dfa2b 100644 --- a/src/elm/Components/Logs.elm +++ b/src/elm/Components/Logs.elm @@ -152,7 +152,7 @@ viewLine shared props logLine lineNumber = [ button [ Util.onClickPreventDefault <| props.msgs.pushUrlHash - { hash = Focus.lineRangeId props.resourceType props.resourceNumber lineNumber props.lineFocus shared.shift + { hash = Focus.resourceLineRangeId props.resourceType props.resourceNumber lineNumber props.lineFocus shared.shift } , Util.testAttribute <| String.join "-" [ "log", "line", "num", props.resourceType, props.resourceNumber, String.fromInt lineNumber ] , id <| Focus.resourceAndLineToFocusId props.resourceType props.resourceNumber lineNumber diff --git a/src/elm/Pages/Org_/Repo_/Build_.elm b/src/elm/Pages/Org_/Repo_/Build_.elm index 4612c309f..fb01acf87 100644 --- a/src/elm/Pages/Org_/Repo_/Build_.elm +++ b/src/elm/Pages/Org_/Repo_/Build_.elm @@ -12,8 +12,8 @@ import Debug exposing (log) import Dict exposing (Dict) import Effect exposing (Effect) import FeatherIcons -import Html exposing (Html, code, details, div, small, summary, text) -import Html.Attributes exposing (attribute, class, id) +import Html exposing (Html, button, code, details, div, small, summary, text) +import Html.Attributes exposing (attribute, class, classList, id) import Html.Events exposing (onClick) import Http import Http.Detailed @@ -83,7 +83,7 @@ init shared route () = , logs = Dict.empty , logLineFocus = route.hash - |> Focus.parseFocusFragment + |> Focus.parseResourceFocusTargetFromFragment |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) , logFollow = 0 } @@ -129,10 +129,27 @@ update shared route msg model = ( { model | logLineFocus = route.hash - |> Focus.parseFocusFragment + |> Focus.parseResourceFocusTargetFromFragment |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) } - , Effect.none + , case model.steps of + RemoteData.Success steps -> + let + resourceNumber = + route.hash + |> Focus.parseResourceFocusTargetFromFragment + |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) + |> Tuple.first + |> Maybe.withDefault -1 + in + steps + |> List.filter (\s -> resourceNumber == s.number) + |> List.map (\s -> ExpandStep { step = s, updateUrlHash = False }) + |> List.map Effect.sendMsg + |> Effect.batch + + _ -> + Effect.none ) PushUrlHash options -> @@ -201,7 +218,10 @@ update shared route msg model = model.logs in ( { model | logs = logs } - , Effect.none + , model.logLineFocus + |> Focus.resourceLineFocusToFocusId "step" + |> (\t -> FocusOn { target = t }) + |> Effect.sendMsg ) Err error -> @@ -242,7 +262,7 @@ update shared route msg model = , buildNumber = route.params.buildNumber } , query = route.query - , hash = Just <| "step:" ++ String.fromInt options.step.number + , hash = Just <| Focus.resourceFocusId "step" (String.fromInt options.step.number) } else @@ -324,14 +344,14 @@ view shared route model = , class "flowline-left" , Util.testAttribute "log-actions" ] - [ Html.button + [ button [ class "button" , class "-link" , onClick CollapseAll , Util.testAttribute "collapse-all" ] [ small [] [ text "collapse all" ] ] - , Html.button + , button [ class "button" , class "-link" , onClick ExpandAll @@ -374,11 +394,17 @@ viewStep shared model route step = else ExpandStep in - div [ Html.Attributes.classList [ ( "step", True ), ( "flowline-left", True ) ], Util.testAttribute "step" ] + div + [ classList + [ ( "step", True ) + , ( "flowline-left", True ) + ] + , Util.testAttribute "step" + ] [ div [ class "-status" ] [ div [ class "-icon-container" ] [ Components.Svgs.statusToIcon step.status ] ] , details - (Html.Attributes.classList + (classList [ ( "details", True ) , ( "-with-border", True ) , ( "-running", step.status == Vela.Running ) diff --git a/src/elm/Pages/Org_/Repo_/Build_/Graph.elm b/src/elm/Pages/Org_/Repo_/Build_/Graph.elm index 41e061e91..28076e67e 100644 --- a/src/elm/Pages/Org_/Repo_/Build_/Graph.elm +++ b/src/elm/Pages/Org_/Repo_/Build_/Graph.elm @@ -95,7 +95,7 @@ init shared route () = , logs = Dict.empty , lineFocus = route.hash - |> Focus.parseFocusFragment + |> Focus.parseResourceFocusTargetFromFragment |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) , logFollow = 0 } @@ -141,7 +141,7 @@ update shared route msg model = ( { model | lineFocus = route.hash - |> Focus.parseFocusFragment + |> Focus.parseResourceFocusTargetFromFragment |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) } , Effect.none diff --git a/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm b/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm index f7d55e7e2..2b62ed85f 100644 --- a/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm +++ b/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm @@ -11,8 +11,8 @@ import Auth import Dict exposing (Dict) import Effect exposing (Effect) import FeatherIcons -import Html exposing (Html, a, button, code, div, small, span, strong, td, text, tr) -import Html.Attributes exposing (attribute, class, id) +import Html exposing (Html, a, button, code, details, div, small, span, strong, summary, table, td, text, tr) +import Html.Attributes exposing (attribute, class, href, id, target) import Html.Events exposing (onClick) import Http import Http.Detailed @@ -74,7 +74,7 @@ type alias Model = { build : WebData Vela.Build , pipeline : WebData Vela.PipelineConfig , templates : WebData (Dict String Vela.Template) - , lineFocus : ( Maybe Int, ( Maybe Int, Maybe Int ) ) + , lineFocus : ( Maybe Int, Maybe Int ) , showTemplates : Bool , expand : Bool , expanding : Bool @@ -88,8 +88,8 @@ init shared route () = , templates = RemoteData.Loading , lineFocus = route.hash - |> Focus.parseFocusFragment - |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) + |> Focus.parseFocusTargetFromFragment + |> (\ft -> ( ft.lineA, ft.lineB )) , showTemplates = True , expand = route.query @@ -201,8 +201,8 @@ update shared route msg model = ( { model | lineFocus = route.hash - |> Focus.parseFocusFragment - |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) + |> Focus.parseFocusTargetFromFragment + |> (\ft -> ( ft.lineA, ft.lineB )) } , Effect.none ) @@ -281,7 +281,10 @@ update shared route msg model = } , expanding = False } - , Effect.none + , model.lineFocus + |> Focus.lineFocusToFocusId + |> (\t -> FocusOn { target = t }) + |> Effect.sendMsg ) Err error -> @@ -383,7 +386,7 @@ view shared route model = RemoteData.Success pipeline -> if String.length pipeline.decodedData > 0 then div [ class "logs-container", class "-pipeline" ] - [ Html.table + [ table [ class "logs-table" ] [ div [ class "header" ] @@ -436,7 +439,7 @@ view shared route model = ] ] , div [ class "logs", Util.testAttribute "pipeline-configuration-data" ] <| - viewLines shared pipeline (Just <| Tuple.second model.lineFocus) + viewLines shared pipeline model.lineFocus ] ] @@ -474,8 +477,8 @@ viewTemplate ( _, t ) = [ span [] [ text t.name ] , span [] [ text t.source ] , a - [ Html.Attributes.target "_blank" - , Html.Attributes.href t.link + [ target "_blank" + , href t.link ] [ text t.link ] ] @@ -484,13 +487,13 @@ viewTemplate ( _, t ) = viewTemplatesDetails : Html.Attribute msg -> Bool -> msg -> List (Html msg) -> Html msg viewTemplatesDetails cls open showHide content = - Html.details + details (class "details" :: class "templates" :: Util.testAttribute "pipeline-templates" :: Util.open open ) - [ Html.summary [ class "summary", Util.onClickPreventDefault showHide ] + [ summary [ class "summary", Util.onClickPreventDefault showHide ] [ div [] [ text "Templates" ] , FeatherIcons.chevronDown |> FeatherIcons.withSize 20 |> FeatherIcons.withClass "details-icon-expand" |> FeatherIcons.toHtml [] ] @@ -498,7 +501,7 @@ viewTemplatesDetails cls open showHide content = ] -viewLines : Shared.Model -> Vela.PipelineConfig -> Maybe Focus.LineFocus -> List (Html Msg) +viewLines : Shared.Model -> Vela.PipelineConfig -> Focus.LineFocus -> List (Html Msg) viewLines shared config lineFocus = config.decodedData |> Utils.Ansi.decodeAnsi @@ -507,49 +510,44 @@ viewLines shared config lineFocus = Just <| viewLine shared - "0" (idx + 1) (Just line) - "0" lineFocus ) |> Array.toList |> List.filterMap identity -viewLine : Shared.Model -> String -> Int -> Maybe Ansi.Log.Line -> String -> Maybe Focus.LineFocus -> Html Msg -viewLine shared id lineNumber line resource lineFocus = +viewLine : Shared.Model -> Int -> Maybe Ansi.Log.Line -> Focus.LineFocus -> Html Msg +viewLine shared lineNumber line lineFocus = tr - [ Html.Attributes.id <| - id - ++ ":" - ++ String.fromInt lineNumber + [ id <| String.fromInt lineNumber , class "line" ] [ case line of Just l -> div [ class "wrapper" - , Util.testAttribute <| String.join "-" [ "config", "line", resource, String.fromInt lineNumber ] - , class <| Focus.lineFocusStyles lineFocus lineNumber + , Util.testAttribute <| String.join "-" [ "config", "line", String.fromInt lineNumber ] + , class <| Focus.lineFocusStyles (Just lineFocus) lineNumber ] [ td [] [ button [ Util.onClickPreventDefault <| PushUrlHash - { hash = Focus.lineRangeId "pipeline" "0" lineNumber lineFocus shared.shift + { hash = Focus.lineRangeId lineNumber (Just lineFocus) shared.shift } - , Util.testAttribute <| String.join "-" [ "config", "line", "num", resource, String.fromInt lineNumber ] - , Html.Attributes.id <| Focus.resourceAndLineToFocusId "config" resource lineNumber + , Util.testAttribute <| String.join "-" [ "config", "line", "num", String.fromInt lineNumber ] + , id <| Focus.lineNumberToFocusId lineNumber , class "line-number" , class "button" , class "-link" - , attribute "aria-label" <| "focus resource " ++ resource + , attribute "aria-label" "focus this line" ] [ span [] [ text <| String.fromInt lineNumber ] ] ] , td [ class "break-text", class "overflow-auto" ] - [ code [ Util.testAttribute <| String.join "-" [ "config", "data", resource, String.fromInt lineNumber ] ] + [ code [ Util.testAttribute <| String.join "-" [ "config", "data", String.fromInt lineNumber ] ] [ Ansi.Log.viewLine l ] ] diff --git a/src/elm/Pages/Org_/Repo_/Build_/Services.elm b/src/elm/Pages/Org_/Repo_/Build_/Services.elm index 3cbac7123..795da610d 100644 --- a/src/elm/Pages/Org_/Repo_/Build_/Services.elm +++ b/src/elm/Pages/Org_/Repo_/Build_/Services.elm @@ -12,8 +12,8 @@ import Debug exposing (log) import Dict exposing (Dict) import Effect exposing (Effect) import FeatherIcons -import Html exposing (Html, code, details, div, small, summary, text) -import Html.Attributes exposing (attribute, class, id) +import Html exposing (Html, button, code, details, div, small, summary, text) +import Html.Attributes exposing (attribute, class, classList, id) import Html.Events exposing (onClick) import Http import Http.Detailed @@ -72,7 +72,7 @@ toLayout user route model = type alias Model = { services : WebData (List Vela.Service) , logs : Dict Int (WebData Vela.Log) - , lineFocus : ( Maybe Int, ( Maybe Int, Maybe Int ) ) + , logLineFocus : ( Maybe Int, ( Maybe Int, Maybe Int ) ) , logFollow : Int } @@ -81,9 +81,9 @@ init : Shared.Model -> Route { org : String, repo : String, buildNumber : String init shared route () = ( { services = RemoteData.Loading , logs = Dict.empty - , lineFocus = + , logLineFocus = route.hash - |> Focus.parseFocusFragment + |> Focus.parseResourceFocusTargetFromFragment |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) , logFollow = 0 } @@ -127,12 +127,29 @@ update shared route msg model = -- BROWSER OnHashChanged _ -> ( { model - | lineFocus = + | logLineFocus = route.hash - |> Focus.parseFocusFragment + |> Focus.parseResourceFocusTargetFromFragment |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) } - , Effect.none + , case model.services of + RemoteData.Success services -> + let + resourceNumber = + route.hash + |> Focus.parseResourceFocusTargetFromFragment + |> (\ft -> ( ft.resourceNumber, ( ft.lineA, ft.lineB ) )) + |> Tuple.first + |> Maybe.withDefault -1 + in + services + |> List.filter (\s -> resourceNumber == s.number) + |> List.map (\s -> ExpandService { service = s, updateUrlHash = False }) + |> List.map Effect.sendMsg + |> Effect.batch + + _ -> + Effect.none ) PushUrlHash options -> @@ -161,7 +178,7 @@ update shared route msg model = services |> List.map (\service -> - case model.lineFocus of + case model.logLineFocus of ( Just resourceNumber, _ ) -> if service.number == resourceNumber then ( { service | viewing = True } @@ -201,7 +218,10 @@ update shared route msg model = model.logs in ( { model | logs = logs } - , Effect.none + , model.logLineFocus + |> Focus.resourceLineFocusToFocusId "service" + |> (\t -> FocusOn { target = t }) + |> Effect.sendMsg ) Err error -> @@ -242,7 +262,7 @@ update shared route msg model = , buildNumber = route.params.buildNumber } , query = route.query - , hash = Just <| "service:" ++ String.fromInt options.service.number + , hash = Just <| Focus.resourceFocusId "service" (String.fromInt options.service.number) } else @@ -324,14 +344,14 @@ view shared route model = , class "flowline-left" , Util.testAttribute "log-actions" ] - [ Html.button + [ button [ class "button" , class "-link" , onClick CollapseAll , Util.testAttribute "collapse-all" ] [ small [] [ text "collapse all" ] ] - , Html.button + , button [ class "button" , class "-link" , onClick ExpandAll @@ -369,11 +389,11 @@ viewService shared model route service = else ExpandService in - div [ Html.Attributes.classList [ ( "service", True ), ( "flowline-left", True ) ], Util.testAttribute "service" ] + div [ classList [ ( "service", True ), ( "flowline-left", True ) ], Util.testAttribute "service" ] [ div [ class "-status" ] [ div [ class "-icon-container" ] [ Components.Svgs.statusToIcon service.status ] ] , details - (Html.Attributes.classList + (classList [ ( "details", True ) , ( "-with-border", True ) , ( "-running", service.status == Vela.Running ) @@ -437,8 +457,8 @@ viewLogs shared model route service log = , resourceType = "service" , resourceNumber = String.fromInt service.number , lineFocus = - if service.number == Maybe.withDefault -1 (Tuple.first model.lineFocus) then - Just <| Tuple.second model.lineFocus + if service.number == Maybe.withDefault -1 (Tuple.first model.logLineFocus) then + Just <| Tuple.second model.logLineFocus else Nothing diff --git a/src/elm/Utils/Focus.elm b/src/elm/Utils/Focus.elm index 4e2729fee..4a2f7d249 100644 --- a/src/elm/Utils/Focus.elm +++ b/src/elm/Utils/Focus.elm @@ -8,10 +8,15 @@ module Utils.Focus exposing , LineFocus , focusFragmentToFocusId , lineFocusStyles + , lineFocusToFocusId + , lineNumberToFocusId , lineRangeId - , parseFocusFragment + , parseFocusTargetFromFragment + , parseResourceFocusTargetFromFragment , resourceAndLineToFocusId - , resourceFocusFragment + , resourceFocusId + , resourceLineFocusToFocusId + , resourceLineRangeId , resourceToFocusId ) @@ -30,13 +35,6 @@ type alias FocusTarget = } -{-| resourceFocusFragment : takes resource tag and maybe line numbers and produces URL fragment for focusing line ranges --} -resourceFocusFragment : String -> String -> List String -> String -resourceFocusFragment resource resourceId args = - String.join ":" <| resource :: resourceId :: args - - {-| resourceToFocusId : takes resource and id and returns the resource focus id for auto focusing on page load -} resourceToFocusId : String -> String -> String @@ -44,6 +42,38 @@ resourceToFocusId resource resourceNumber = String.join "-" [ resource, resourceNumber ] +{-| lineNumberToFocusId : takes resource, id and line number and returns the line focus id for auto focusing on page load +-} +lineNumberToFocusId : Int -> String +lineNumberToFocusId lineNumber = + String.join "-" [ "line", String.fromInt lineNumber ] + + +{-| lineFocusToFocusId : takes resource, id and line number and returns the line focus id for auto focusing on page load +-} +lineFocusToFocusId : LineFocus -> String +lineFocusToFocusId lineFocus = + lineFocus + |> Tuple.first + |> List.singleton + |> List.filterMap identity + |> List.map String.fromInt + |> (::) "line" + |> String.join "-" + + +{-| resourceLineFocusToFocusId : takes resource, id and line number and returns the line focus id for auto focusing on page load +-} +resourceLineFocusToFocusId : String -> ( Maybe Int, LineFocus ) -> String +resourceLineFocusToFocusId resource resourceLineFocus = + resourceLineFocus + |> (\( resourceNumber, lineFocus ) -> ( resourceNumber, Tuple.first lineFocus )) + |> Tuple.mapBoth (Maybe.map String.fromInt) (Maybe.map String.fromInt) + |> (\( resourceNumber, maybeLineA ) -> [ Just resource, resourceNumber, Just "line", maybeLineA ]) + |> List.filterMap identity + |> String.join "-" + + {-| resourceAndLineToFocusId : takes resource, id and line number and returns the line focus id for auto focusing on page load -} resourceAndLineToFocusId : String -> String -> Int -> String @@ -57,7 +87,7 @@ focusFragmentToFocusId : String -> Maybe String -> String focusFragmentToFocusId resource focusFragment = let parsed = - parseFocusFragment focusFragment + parseResourceFocusTargetFromFragment focusFragment in case ( parsed.resourceNumber, parsed.lineA, parsed.lineB ) of ( Just resourceNumber, Just lineA, Nothing ) -> @@ -73,10 +103,25 @@ focusFragmentToFocusId resource focusFragment = "" -{-| parseFocusFragment : takes URL fragment and parses it into appropriate line focus chunks +{-| parseFocusTargetFromFragment : takes URL fragment and parses it into appropriate line focus chunks -} -parseFocusFragment : Maybe String -> FocusTarget -parseFocusFragment focusFragment = +parseFocusTargetFromFragment : Maybe String -> FocusTarget +parseFocusTargetFromFragment focusFragment = + case String.split ":" (Maybe.withDefault "" focusFragment) of + lineA :: lineB :: _ -> + { target = Nothing, resourceNumber = Nothing, lineA = String.toInt lineA, lineB = String.toInt lineB } + + lineA :: _ -> + { target = Nothing, resourceNumber = Nothing, lineA = String.toInt lineA, lineB = Nothing } + + _ -> + { target = Nothing, resourceNumber = Nothing, lineA = Nothing, lineB = Nothing } + + +{-| parseResourceFocusTargetFromFragment : takes URL fragment and parses it into appropriate line focus chunks +-} +parseResourceFocusTargetFromFragment : Maybe String -> FocusTarget +parseResourceFocusTargetFromFragment focusFragment = case String.split ":" (Maybe.withDefault "" focusFragment) of target :: resourceNumber :: lineA :: lineB :: _ -> { target = Just target, resourceNumber = String.toInt resourceNumber, lineA = String.toInt lineA, lineB = String.toInt lineB } @@ -91,11 +136,11 @@ parseFocusFragment focusFragment = { target = Nothing, resourceNumber = Nothing, lineA = Nothing, lineB = Nothing } -{-| lineRangeId : takes resource, line, and focus information and returns the fragment for focusing a range of lines +{-| lineRangeId : takes line and focus information and returns the fragment for focusing a range of lines -} -lineRangeId : String -> String -> Int -> Maybe LineFocus -> Bool -> String -lineRangeId resource resourceNumber lineNumber maybeLineFocus shiftDown = - resourceFocusFragment resource resourceNumber <| +lineRangeId : Int -> Maybe LineFocus -> Bool -> String +lineRangeId lineNumber maybeLineFocus shiftDown = + String.join ":" <| List.map String.fromInt (List.sort <| case maybeLineFocus of @@ -123,6 +168,47 @@ lineRangeId resource resourceNumber lineNumber maybeLineFocus shiftDown = ) +{-| resourceFocusId : takes resource and returns the fragment for focusing a resource +-} +resourceFocusId : String -> String -> String +resourceFocusId resource resourceNumber = + String.join ":" [ resource, resourceNumber ] + + +{-| resourceLineRangeId : takes resource, line, and focus information and returns the fragment for focusing a range of lines +-} +resourceLineRangeId : String -> String -> Int -> Maybe LineFocus -> Bool -> String +resourceLineRangeId resource resourceNumber lineNumber maybeLineFocus shiftDown = + String.join ":" <| + resource + :: resourceNumber + :: List.map String.fromInt + (List.sort <| + case maybeLineFocus of + Just lineFocus -> + case ( shiftDown, lineFocus ) of + ( True, ( Just lineA, Just lineB ) ) -> + if lineNumber < lineA then + [ lineNumber, lineB ] + + else + [ lineA, lineNumber ] + + ( True, ( Just lineA, _ ) ) -> + if lineNumber < lineA then + [ lineNumber, lineA ] + + else + [ lineA, lineNumber ] + + _ -> + [ lineNumber ] + + Nothing -> + [ lineNumber ] + ) + + {-| lineFocusStyles : takes maybe linefocus and linenumber and returns the appropriate style for highlighting a focused line -} lineFocusStyles : Maybe LineFocus -> Int -> String