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

Commit 39ba3c0

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 39ba3c0

File tree

14 files changed

+315
-35
lines changed

14 files changed

+315
-35
lines changed

src/App.elm

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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,31 @@ 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+
Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version"
529+
|> Button.small
530+
|> Button.view
531+
|> List.singleton
532+
|> Sidebar.headerItem []
515533
in
516-
header
517-
[ classList [ ( "perspective", True ), ( "is-overflowing", isOverflowing ) ] ]
518-
[ UI.namespaceSlug
519-
, h2 [ class "namespace" ] [ FQN.view fqn ]
534+
Sidebar.header
535+
[ Sidebar.headerItem
536+
[ classList [ ( "is-overflowing", isOverflowing ) ] ]
537+
[ UI.namespaceSlug
538+
, h2 [ class "namespace" ] [ FQN.view fqn ]
539+
]
540+
, download
541+
, UI.divider
520542
]
521543

522544

@@ -587,7 +609,7 @@ viewMainSidebar model =
587609
Sidebar.view
588610
[ viewMainSidebarCollapseButton model
589611
, div [ class "expanded-content" ]
590-
[ viewPerspective model.env
612+
[ viewSidebarHeader model.env
591613
, div [ class "sidebar-scroll-area" ]
592614
[ sidebarContent
593615
, Sidebar.section
@@ -621,6 +643,33 @@ viewMainSidebar model =
621643
]
622644

623645

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

822+
DownloadModal fqn ->
823+
viewDownloadModal fqn
824+
773825

774826
viewAppLoading : AppContext -> Html msg
775827
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)