【问题标题】:How can I make a draggable split panel with Elm (preferably Elm-UI)?如何使用 Elm(最好是 Elm-UI)制作可拖动的拆分面板?
【发布时间】:2020-09-22 19:24:27
【问题描述】:

我为 Elm 寻找了一个拆分窗格,发现一些东西似乎不太容易使用。

doodledood/elm-split-pane 与 Elm 0.19.1 不兼容,所以我想我要么必须切换到 Elm 0.18,要么将其转换为 Elm 0.19.1 才能使用它。它也是根据 Elm HTML 而不是 Elm UI 定义的。

我还找到了https://github.com/ellie-app/ellie/blob/master/assets/src/Ellie/Ui/SplitPane.elm。这涉及到一堆自定义 JavaScript,而且对我来说它与应用程序的其余部分的耦合程度并不明显。

【问题讨论】:

    标签: elm


    【解决方案1】:

    我正在使用 elm-ui 在我的 elm 项目中使用拆分窗格。我采用了这个基于 SplitPane elm-html 的包 https://github.com/doodledood/elm-split-pane,将其源代码复制到我的项目中,并通过包装内容和属性将其内部转换为从其视图函数返回 Element msg。

    这里是转换后的函数的源代码:

    view : ViewConfig msg -> Element msg -> Element msg -> State -> Element msg
    view (ViewConfig viewConfig) firstView secondView (State state) =
        let
            splitter =
                getConcreteSplitter viewConfig state.orientation state.dragState
        in
        case state.orientation of
            Horizontal ->
                row
                    (paneContainerStyle state.orientation
                        ++ [ width fill ]
                    )
                    [ el (firstChildViewStyle (State state)) firstView
                    , splitter
                    , el (secondChildViewStyle (State state)) secondView
                    ]
    
            Vertical ->
                column (paneContainerStyle state.orientation)
                    [ el (firstChildViewStyle (State state)) firstView
                    , splitter
                    , el (secondChildViewStyle (State state)) secondView
                    ]
    
    
    viewReversed : ViewConfig msg -> Element msg -> Element msg -> State -> Element msg
    viewReversed (ViewConfig viewConfig) firstView secondView (State state) =
        let
            splitter =
                getConcreteSplitter viewConfig state.orientation state.dragState
        in
        case state.orientation of
            Horizontal ->
                row
                    (paneContainerStyle state.orientation
                        ++ [ width fill ]
                    )
                    [ el (secondChildViewStyle (State state)) secondView
                    , splitter
                    , el (firstChildViewStyle (State state)) firstView
                    ]
    
            Vertical ->
                column (paneContainerStyle state.orientation)
                    [ el (secondChildViewStyle (State state)) firstView
                    , splitter
                    , el (firstChildViewStyle (State state)) firstView
                    ]
    
    
    getConcreteSplitter :
        { toMsg : Msg -> msg
        , splitter : Maybe (CustomSplitter msg)
        }
        -> Orientation
        -> DragState
        -> Element msg
    getConcreteSplitter viewConfig orientation4 dragState =
        case viewConfig.splitter of
            Just (CustomSplitter splitter) ->
                splitter
    
            Nothing ->
                case createCustomSplitter viewConfig.toMsg <| createDefaultSplitterDetails orientation4 dragState of
                    CustomSplitter defaultSplitter ->
                        defaultSplitter
    

    以下是样式函数:

    paneContainerStyle : Orientation -> List (Attribute msg)
    paneContainerStyle orientation5 =
        [ style "overflow" "hidden"
        , style "display" "flex"
        , style "flexDirection"
            (case orientation5 of
                Horizontal ->
                    "row"
    
                Vertical ->
                    "column"
            )
        , style "justifyContent" "center"
        , style "alignItems" "center"
        , style "width" "100%"
        , style "height" "100%"
        , style "boxSizing" "border-box"
        ]
            |> List.map Element.htmlAttribute
    
    
    firstChildViewStyle : State -> List (Attribute msg)
    firstChildViewStyle (State state) =
        case state.splitterPosition of
            Px px2 ->
                let
                    v =
                        (String.fromFloat <| toFloat (getValue px2)) ++ "px"
                in
                case state.orientation of
                    Horizontal ->
                        [ style "display" "flex"
                        , style "width" v
                        , style "height" "100%"
                        , style "overflow" "hidden"
                        , style "boxSizing" "border-box"
                        , style "position" "relative"
                        ]
                            |> List.map Element.htmlAttribute
    
                    Vertical ->
                        [ style "display" "flex"
                        , style "width" "100%"
                        , style "height" v
                        , style "overflow" "hidden"
                        , style "boxSizing" "border-box"
                        , style "position" "relative"
                        ]
                            |> List.map Element.htmlAttribute
    
            Percentage p ->
                let
                    v =
                        String.fromFloat <| getValue p
                in
                [ style "display" "flex"
                , style "flex" v
                , style "width" "100%"
                , style "height" "100%" -- pz edit
                , style "overflow" "hidden"
                , style "boxSizing" "border-box"
                , style "position" "relative"
                ]
                    |> List.map Element.htmlAttribute
    
    
    secondChildViewStyle : State -> List (Attribute msg)
    secondChildViewStyle (State state) =
        case state.splitterPosition of
            Px _ ->
                [ style "display" "flex"
                , style "flex" "1"
                , style "width" "100%"
                , style "height" "100%"
                , style "overflow" "hidden"
                , style "boxSizing" "border-box"
                , style "position" "relative"
                ]
                    |> List.map Element.htmlAttribute
    
            Percentage p ->
                let
                    v =
                        String.fromFloat <| 1 - getValue p
                in
                [ style "display" "flex"
                , style "flex" v
                , style "width" "100%"
                , style "height" "100%"
                , style "overflow" "hidden"
                , style "boxSizing" "border-box"
                , style "position" "relative"
                ]
                    |> List.map Element.htmlAttribute
    

    乌里

    【讨论】:

      【解决方案2】:

      我尝试了这种方法,但个人发现 html 和 elm-ui 的组合非常混乱。 (这是我,YMMV。)我更简单的方法是使用带有自定义拇指的 elm-ui 滚动条。

      contentArea : Model -> Element Msg
      contentArea model =
          let
              leftPane =
                  column
                      [ width fill, alignTop ]
                      [ ... ]
      
              rightPane =
                      column [ spacing 5, padding 5, alignTop, centerX ]
                          [ ... ]
      
              splitter =
                  Input.slider
                      [ height <| px 2
                      , width <| px 1000
                      , centerY
                      , behindContent <|
                          -- Slider track
                          el
                              [ width fill
                              , height <| px 1
                              , centerY
                              , centerX
                              ]
                              none
                      ]
                      { onChange = ResizeViews << round
                      , label =
                          Input.labelHidden "Splitter"
                      , min = 0.0
                      , max = toFloat 1000
                      , step = Just 1
                      , value = toFloat model.splitInPixels
                      , thumb = customThumb
                      }
      
              customThumb =
                  Input.thumb
                      [ width (px 3)
                      , height (px 600)
                      , moveDown 300
                      , Border.rounded 1
                      , Border.width 1
                      , Border.color (rgb 0.5 0.5 0.5)
                      , Background.color FlatColors.BritishPalette.seabrook
                      ]
          in
          column [ width fill, spacing 5, padding 5 ]
              [ splitter
              , row [ width fill, spacing 5, padding 5 ]
                  [ el [ width <| fillPortion model.splitInPixels, alignTop ] leftPane
                  , el [ width <| fillPortion (1000 - model.splitInPixels), alignTop ] rightPane
                  ]
              ]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-08
        • 2016-06-19
        相关资源
        最近更新 更多