Skip to content
This repository was archived by the owner on Jul 19, 2022. It is now read-only.

Commit 2524205

Browse files
committed
Add DownloadModal with click to copy
Add a new download button that appear under a namespace perspective. Clicking this button will launch a modal that has a "click to copy" functionality. * Copy on click is built with a WebComponent (`CopyOnClick.js`) rendered from Elm via a new `CopyField` module, which includes a new clipboard `Icon`. * The perspective in the sidebar is now rendered via `Sidebar.header` and `Sidebar.headerItem` to include proper padding and a bottom divider.
1 parent 53c0489 commit 2524205

File tree

14 files changed

+312
-36
lines changed

14 files changed

+312
-36
lines changed

src/App.elm

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Env exposing (AppContext(..), Env, OperatingSystem(..))
99
import Finder
1010
import Finder.SearchOptions as SearchOptions
1111
import FullyQualifiedName as FQN exposing (FQN)
12-
import Html exposing (Html, a, div, h1, h2, h3, header, nav, p, section, span, strong, text)
12+
import Html exposing (Html, a, div, h1, h2, h3, nav, p, section, span, strong, text)
1313
import Html.Attributes exposing (class, classList, href, id, rel, target, title)
1414
import Html.Events exposing (onClick)
1515
import Http
@@ -26,6 +26,7 @@ import UI.AppHeader as AppHeader
2626
import UI.Banner as Banner
2727
import UI.Button as Button
2828
import UI.Click as Click exposing (Click(..))
29+
import UI.CopyField as CopyField
2930
import UI.Icon as Icon
3031
import UI.Modal as Modal
3132
import UI.Sidebar as Sidebar
@@ -46,6 +47,7 @@ type Modal
4647
| HelpModal
4748
| ReportBugModal
4849
| PublishModal
50+
| DownloadModal FQN
4951

5052

5153
type alias Model =
@@ -498,8 +500,8 @@ viewAppHeader model =
498500
}
499501

500502

501-
viewPerspective : Env -> Html Msg
502-
viewPerspective env =
503+
viewSidebarHeader : Env -> Html Msg
504+
viewSidebarHeader env =
503505
case env.perspective of
504506
Codebase _ ->
505507
UI.nothing
@@ -512,11 +514,27 @@ viewPerspective env =
512514
-- thats quite involved...
513515
isOverflowing =
514516
fqn |> FQN.toString |> String.length |> (\l -> l > 20)
517+
518+
download =
519+
case env.appContext of
520+
UnisonShare ->
521+
Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version"
522+
|> Button.small
523+
|> Button.view
524+
|> List.singleton
525+
|> Sidebar.headerItem []
526+
527+
Ucm ->
528+
UI.nothing
515529
in
516-
header
517-
[ classList [ ( "perspective", True ), ( "is-overflowing", isOverflowing ) ] ]
518-
[ UI.namespaceSlug
519-
, h2 [ class "namespace" ] [ FQN.view fqn ]
530+
Sidebar.header
531+
[ Sidebar.headerItem
532+
[ classList [ ( "is-overflowing", isOverflowing ) ] ]
533+
[ UI.namespaceSlug
534+
, h2 [ class "namespace" ] [ FQN.view fqn ]
535+
]
536+
, download
537+
, UI.divider
520538
]
521539

522540

@@ -587,7 +605,7 @@ viewMainSidebar model =
587605
Sidebar.view
588606
[ viewMainSidebarCollapseButton model
589607
, div [ class "expanded-content" ]
590-
[ viewPerspective model.env
608+
[ viewSidebarHeader model.env
591609
, div [ class "sidebar-scroll-area" ]
592610
[ sidebarContent
593611
, Sidebar.section
@@ -621,6 +639,33 @@ viewMainSidebar model =
621639
]
622640

623641

642+
viewDownloadModal : FQN -> Html Msg
643+
viewDownloadModal fqn =
644+
let
645+
prettyName =
646+
FQN.toString fqn
647+
648+
unqualified =
649+
FQN.unqualifiedName fqn
650+
651+
pullCommand =
652+
"pull git@github.com:unisonweb/share.git:." ++ prettyName ++ " ." ++ unqualified
653+
654+
content =
655+
Modal.Content
656+
(section
657+
[]
658+
[ p [] [ text "Download ", UI.bold prettyName, text " by pulling the namespace from Unison Share into a namespace in your local codebase:" ]
659+
, CopyField.copyField (\_ -> CloseModal) pullCommand |> CopyField.withPrefix ".>" |> CopyField.view
660+
, div [ class "hint" ] [ text "Copy and paste this command into UCM." ]
661+
]
662+
)
663+
in
664+
Modal.modal "download-modal" CloseModal content
665+
|> Modal.withHeader ("Download " ++ prettyName)
666+
|> Modal.view
667+
668+
624669
viewHelpModal : OperatingSystem -> KeyboardShortcut.Model -> Html Msg
625670
viewHelpModal os keyboardShortcut =
626671
let
@@ -770,6 +815,9 @@ viewModal model =
770815
ReportBugModal ->
771816
viewReportBugModal model.env.appContext
772817

818+
DownloadModal fqn ->
819+
viewDownloadModal fqn
820+
773821

774822
viewAppLoading : AppContext -> Html msg
775823
viewAppLoading appContext =

src/UI.elm

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module UI exposing (..)
22

3-
import Html exposing (Attribute, Html, code, div, hr, pre, span, text)
3+
import Html exposing (Attribute, Html, code, div, hr, pre, span, strong, text)
44
import Html.Attributes exposing (class)
55
import Html.Events exposing (onClick)
66
import UI.Icon as Icon
@@ -11,6 +11,11 @@ codeBlock attrs code_ =
1111
pre attrs [ code [] [ code_ ] ]
1212

1313

14+
bold : String -> Html msg
15+
bold text_ =
16+
strong [] [ text text_ ]
17+
18+
1419
inlineCode : List (Attribute msg) -> Html msg -> Html msg
1520
inlineCode attrs code_ =
1621
code (class "inline-code" :: attrs) [ code_ ]
@@ -60,7 +65,7 @@ emptyStateMessage message =
6065

6166
divider : Html msg
6267
divider =
63-
hr [] []
68+
hr [ class "divider" ] []
6469

6570

6671
charWidth : Int -> String

src/UI/CopyField.elm

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
module UI.CopyField exposing (..)
2+
3+
import Html exposing (Html, button, div, input, node, text)
4+
import Html.Attributes exposing (attribute, class, readonly, type_, value)
5+
import UI
6+
import UI.Icon as Icon
7+
8+
9+
type alias CopyField msg =
10+
{ prefix : Maybe String
11+
, toCopy : String
12+
, onCopy : String -> msg
13+
}
14+
15+
16+
copyField : (String -> msg) -> String -> CopyField msg
17+
copyField onCopy toCopy =
18+
{ prefix = Nothing, toCopy = toCopy, onCopy = onCopy }
19+
20+
21+
withPrefix : String -> CopyField msg -> CopyField msg
22+
withPrefix prefix field =
23+
{ field | prefix = Just prefix }
24+
25+
26+
withToCopy : String -> CopyField msg -> CopyField msg
27+
withToCopy toCopy field =
28+
{ field | toCopy = toCopy }
29+
30+
31+
view : CopyField msg -> Html msg
32+
view field =
33+
let
34+
prefix =
35+
field.prefix
36+
|> Maybe.map (\p -> div [ class "copy-field-prefix" ] [ text p ])
37+
|> Maybe.withDefault UI.nothing
38+
in
39+
div [ class "copy-field" ]
40+
[ div [ class "copy-field-field" ]
41+
[ prefix
42+
, div
43+
[ class "copy-field-input" ]
44+
[ input
45+
[ type_ "text"
46+
, class "copy-field-to-copy"
47+
, value field.toCopy
48+
, readonly True
49+
]
50+
[]
51+
]
52+
]
53+
, copyButton field.toCopy
54+
]
55+
56+
57+
58+
-- HELPERS --------------------------------------------------------------------
59+
60+
61+
{-| We're not using UI.Button here since a click handler is added from
62+
the webcomponent in JS land.
63+
-}
64+
copyButton : String -> Html msg
65+
copyButton toCopy =
66+
node "copy-on-click"
67+
[ attribute "text" toCopy ]
68+
[ button [ class "button contained default" ] [ Icon.view Icon.clipboard ]
69+
]

src/UI/CopyOnClick.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// <copy-on-click text="text-to-copy">
2+
// clickable content
3+
// </copy-on-click>
4+
//
5+
// Use from Elm with an Icon:
6+
// node "copy-on-click" [ ] [ UI.Icon.view UI.Icon.clipboard ]
7+
class CopyOnClick extends HTMLElement {
8+
constructor() {
9+
super();
10+
}
11+
12+
connectedCallback() {
13+
this.addEventListener("click", () => {
14+
const text = this.getAttribute("text");
15+
16+
// writeText returns a promise with success/failure that we should
17+
// probably do something with...
18+
navigator.clipboard.writeText(text);
19+
});
20+
}
21+
22+
static get observedAttributes() {
23+
return ["text"];
24+
}
25+
}
26+
27+
customElements.define("copy-on-click", CopyOnClick);

src/UI/Icon.elm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,12 @@ tagsOutlined =
370370
, path [ fill "currentColor", fillRule "evenodd", d "M8.62836 2.16552C8.81309 1.96026 9.12923 1.94362 9.33449 2.12835L13.6332 5.99715C14.2238 6.52872 14.2974 7.42865 13.801 8.04914L10.3904 12.3123C10.2179 12.528 9.90329 12.5629 9.68766 12.3904C9.47203 12.2179 9.43707 11.9033 9.60957 11.6877L13.0201 7.42444C13.1856 7.21761 13.1611 6.91763 12.9642 6.74045L8.66552 2.87165C8.46027 2.68692 8.44363 2.37077 8.62836 2.16552Z" ] []
371371
, circle [ cx "4", cy "5", r "1.5", stroke "currentColor", fill "transparent" ] []
372372
]
373+
374+
375+
clipboard : Icon msg
376+
clipboard =
377+
Icon "clipboard"
378+
[]
379+
[ path [ fill "currentColor", fillRule "evenodd", d "M8 2.25C8 2.11193 7.88807 2 7.75 2H6.25C6.11193 2 6 2.11193 6 2.25V2.75C6 2.88807 6.11193 3 6.25 3H7.75C7.88807 3 8 2.88807 8 2.75V2.25ZM6 1C5.44772 1 5 1.44772 5 2V3C5 3.55228 5.44772 4 6 4H8C8.55228 4 9 3.55228 9 3V2C9 1.44772 8.55228 1 8 1H6Z" ] []
380+
, path [ fill "currentColor", fillRule "evenodd", d "M3 2.5C3 2.22386 3.22386 2 3.5 2C3.77614 2 4 2.22386 4 2.5V10.5C4 10.7761 4.22386 11 4.5 11H9.5C9.77614 11 10 10.7761 10 10.5V2.5C10 2.22386 10.2239 2 10.5 2C10.7761 2 11 2.22386 11 2.5V11C11 11.5523 10.5523 12 10 12H4C3.44772 12 3 11.5523 3 11V2.5Z" ] []
381+
]

src/UI/Sidebar.elm

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
module UI.Sidebar exposing (..)
22

3-
import Html exposing (Html, a, aside, h3, label, text)
3+
import Html exposing (Attribute, Html, a, aside, div, h3, label, text)
44
import Html.Attributes exposing (class, id)
55
import Html.Events exposing (onClick)
66

77

8+
header : List (Html msg) -> Html msg
9+
header content =
10+
Html.header [ class "sidebar-header" ] content
11+
12+
13+
headerItem : List (Attribute msg) -> List (Html msg) -> Html msg
14+
headerItem attrs content =
15+
div (attrs ++ [ class "sidebar-header-item" ]) content
16+
17+
818
section : String -> List (Html msg) -> Html msg
919
section label content =
1020
Html.section [ class "sidebar-section" ]
11-
(header label :: content)
21+
(sectionTitle label :: content)
1222

1323

14-
header : String -> Html msg
15-
header label =
16-
h3 [ class "sidebar-header" ] [ text label ]
24+
sectionTitle : String -> Html msg
25+
sectionTitle label =
26+
h3 [ class "sidebar-section-title" ] [ text label ]
1727

1828

1929
item : msg -> String -> Html msg

0 commit comments

Comments
 (0)