svg.escobar.life

A simple SVG markup editor for the web

Commit
f62c6cb5683b638662387fbe711f3487f140d381
Parent
22809cfba84311ef77dee35c0e0653306a329d6a
Author
Gark Garcia <37553739+GarkGarcia@users.noreply.github.com>
Date

Started working on a syntax-highlighted editor.

Diffstat

5 files changed, 79 insertions, 24 deletions

Status File Name N° Changes Insertions Deletions
Modified elm.json 4 3 1
Modified src/Main.elm 4 4 0
Modified src/Types.elm 16 13 3
Modified src/View.elm 35 30 5
Modified styles.css 44 29 15
diff --git a/elm.json b/elm.json
@@ -13,10 +13,12 @@
             "elm/http": "2.0.0",
             "elm/json": "1.1.3",
             "elm/regex": "1.0.0",
-            "elm/svg": "1.0.1"
+            "elm/svg": "1.0.1",
+            "pablohirafuji/elm-syntax-highlight": "3.1.0"
         },
         "indirect": {
             "elm/bytes": "1.0.8",
+            "elm/parser": "1.1.0",
             "elm/time": "1.0.0",
             "elm/url": "1.0.0",
             "elm/virtual-dom": "1.0.2"
diff --git a/src/Main.elm b/src/Main.elm
@@ -44,6 +44,9 @@ update msg model =
         Upload upl ->
             upload model upl
 
+        Scroll offset ->
+            ({model | offset = offset}, Cmd.none)
+
 load : Result Http.Error String -> Cmd Msg
 load result =
     case result of
@@ -74,6 +77,7 @@ init =
     , darkMode = False
     , uriEncoder = Maybe.withDefault Regex.never <| Regex.fromString "!|#|\\$|%|&|'|\\(|\\)|\\*|\\+|,|\\/|:|;|=|\\?|@|\\[|\\]"
     , fileName = "example.svg"
+    , offset = { top = 0, left = 0 }
     }
 
 loadExample : Cmd Msg
diff --git a/src/Types.elm b/src/Types.elm
@@ -5,14 +5,24 @@ import Regex exposing (Regex)
 import Http
 
 type alias Model =
-    { image : String, isValid : Bool, darkMode : Bool, uriEncoder : Regex, fileName : String }
+    { image : String
+    , isValid : Bool
+    , darkMode : Bool
+    , uriEncoder : Regex
+    , fileName : String
+    , offset : Offset
+    }
 
 type Upload = Requested | Selected File
 
+type alias Offset =
+    { top : Int, left : Int }
+
 type Msg
     = Update String
     | Load (Result Http.Error String)
     | Validation Bool
     | DarkModeToggle
     | Download
-    | Upload Upload-
\ No newline at end of file
+    | Upload Upload
+    | Scroll Offset+
\ No newline at end of file
diff --git a/src/View.elm b/src/View.elm
@@ -1,14 +1,15 @@
 module View exposing (view)
 
 import Types exposing (..)
-import Html exposing (Html, div, img, button, a, textarea)
+import Html exposing (Html, div, img, button, a, textarea, pre, code, text)
 import Html.Events exposing (onClick, onInput, on)
-import Html.Attributes exposing (id, class, src, href, value, spellcheck, placeholder, target, rel)
+import Html.Attributes exposing (id, class, src, href, value, style, spellcheck, placeholder, target, rel)
 import Browser exposing (Document)
 import Svg exposing (svg, path)
 import Svg.Attributes exposing (d, viewBox, fill)
 import Json.Decode
 import Regex exposing (Regex, Match, replace)
+import SyntaxHighlight exposing (useTheme, xml, oneDark, toBlockHtml)
 
 
 view : Model -> Document Msg
@@ -41,11 +42,35 @@ display model =
 
 texteditor : Model -> Html Msg
 texteditor model =
-    textarea 
+    div [ id "editor", onInput Update ]
+        [ useTheme oneDark
+        , xml (model.image ++ (String.fromInt model.offset.left))
+            |> Result.map (toBlockHtml Nothing)
+            |> Result.withDefault
+                (pre (translate model.offset)
+                    [ code [] [ text model.image ]]
+                )
+        , viewTextarea model
+        ]
+
+translate : Offset -> List (Html.Attribute Msg)
+translate { top, left } =
+    [ style "left" ((String.fromInt -top) ++ "px")
+    , style "top"  ((String.fromInt -left) ++ "px")
+    ]
+
+viewTextarea : Model -> Html Msg
+viewTextarea model =
+    textarea
         [ value model.image
-        , placeholder "<svg ...> ... </svg>"
+        , onInput Update
         , spellcheck False
-        , onInput (\input -> Update input)
+        , on "scroll"
+            (Json.Decode.map2 Offset
+                (Json.Decode.at [ "target", "scrollTop" ] Json.Decode.int)
+                (Json.Decode.at [ "target", "scrollLeft" ] Json.Decode.int)
+                |> Json.Decode.map Scroll
+            )
         ] []
 
 uri : Model -> String
diff --git a/styles.css b/styles.css
@@ -4,7 +4,6 @@
     align-self: center;
     align-content: center;
     align-items: center;
-    text-align: center;
     
     cursor: default;
     -webkit-user-select: none; /* Safari */        
@@ -26,6 +25,8 @@
         --large: 10vmax;
         --small: 2.5vmax;
         --tiny: 1vmax;
+
+        font-size: .175in;
     }
 }
 
@@ -34,6 +35,8 @@
         --large: 15vmax;
         --small: 3vmax;
         --tiny: 2vmax;
+
+        font-size: .25in;
     }
 }
 
@@ -235,8 +238,31 @@ body > div#container.error > div#display > svg#error {
     display: block;
 }
 
-body > div#container > textarea {
+body > div#container > div#editor {
     grid-area: text;
+    position: relative;
+
+    width: 100%;
+    height: 100%;
+
+    overflow: hidden;
+}
+
+body > div#container > div#editor * {
+    background: transparent !important;
+
+    font-family: 'B612 Mono', monospace !important;
+    font-style: italic !important;
+}
+
+body > div#container > div#editor > * {
+    position: absolute;
+    margin: 0 !important;
+}
+
+body > div#container > div#editor > textarea {
+    z-index: 1;
+
     resize: none;
     white-space: pre;
 
@@ -250,21 +276,9 @@ body > div#container > textarea {
     
 
     font-family: 'B612 Mono', monospace;
-    color: var(--light);
+    color: transparent;
     font-style: italic;
 
     scrollbar-width: none; /*For Firefox*/
     -ms-overflow-style: none; /*For IE and Edge*/
-}
-
-@media (hover: hover) {
-    body > div#container > textarea {
-        font-size: .175in;
-    }
-}
-
-@media (hover: none) {
-    body > div#container > textarea {
-        font-size: .25in;
-    }
 } 
\ No newline at end of file