- Commit
- f6e575e795989ab7013871c6a6f6d4db05a55363
- Parent
- f62c6cb5683b638662387fbe711f3487f140d381
- Author
- Gark Garcia <37553739+GarkGarcia@users.noreply.github.com>
- Date
Finished implementing syntax-highlighting.
A simple SVG markup editor for the web
Finished implementing syntax-highlighting.
5 files changed, 119 insertions, 73 deletions
Status | File Name | N° Changes | Insertions | Deletions |
Added | src/Editor.elm | 54 | 54 | 0 |
Modified | src/Main.elm | 6 | 3 | 3 |
Modified | src/Types.elm | 9 | 3 | 6 |
Modified | src/View.elm | 37 | 2 | 35 |
Modified | styles.css | 86 | 57 | 29 |
diff --git a/src/Editor.elm b/src/Editor.elm @@ -0,0 +1,53 @@ +module Editor exposing (editor) + +import Types exposing (..) +import Html exposing (Html, Attribute, div, pre, code, text, textarea) +import Html.Attributes exposing (id, value, style, spellcheck) +import Html.Events exposing (onInput, on) +import Json.Decode +import SyntaxHighlight exposing (xml, toBlockHtml) + +editor : Model -> Html Msg +editor model = + div [ id "editor" ] + [ div [ translate model.editorScroll ] + [ codeDisplay model ] + , textarea model + ] + +textarea : Model -> Html Msg +textarea model = + Html.textarea + [ value model.image + , onInput Update + , onScroll Scroll + , spellcheck False + ] [] + +codeDisplay : Model -> Html Msg +codeDisplay model = + xml (if model.image == "" then placeholder else model.image) + |> Result.map (toBlockHtml Nothing) + |> Result.withDefault + (pre [] [ code [] [ text model.image ] ]) + +onScroll : ((Int, Int) -> msg) -> Attribute msg +onScroll f = + on "scroll" + (Json.Decode.map2 (\x -> \y -> (x, y)) + (Json.Decode.at [ "target", "scrollLeft" ] Json.Decode.int) + (Json.Decode.at [ "target", "scrollTop" ] Json.Decode.int) + |> Json.Decode.map f + ) + +transform : String -> Attribute msg +transform str = + style "transform" str + +translate : (Int, Int) -> Attribute Msg +translate (x, y) = + transform ("translate(" ++ (String.fromInt -x) ++ "px, " ++ (String.fromInt -y) ++ "px)") + +placeholder : String +placeholder = + "<svg ...> ... </svg>"+ \ No newline at end of file
diff --git a/src/Main.elm b/src/Main.elm @@ -44,8 +44,8 @@ update msg model = Upload upl -> upload model upl - Scroll offset -> - ({model | offset = offset}, Cmd.none) + Scroll scroll -> + ({model | editorScroll = scroll}, Cmd.none) load : Result Http.Error String -> Cmd Msg load result = @@ -77,7 +77,7 @@ init = , darkMode = False , uriEncoder = Maybe.withDefault Regex.never <| Regex.fromString "!|#|\\$|%|&|'|\\(|\\)|\\*|\\+|,|\\/|:|;|=|\\?|@|\\[|\\]" , fileName = "example.svg" - , offset = { top = 0, left = 0 } + , editorScroll = (0, 0) } loadExample : Cmd Msg
diff --git a/src/Types.elm b/src/Types.elm @@ -10,14 +10,11 @@ type alias Model = , darkMode : Bool , uriEncoder : Regex , fileName : String - , offset : Offset + , editorScroll : (Int, Int) } type Upload = Requested | Selected File -type alias Offset = - { top : Int, left : Int } - type Msg = Update String | Load (Result Http.Error String) @@ -25,4 +22,4 @@ type Msg | DarkModeToggle | Download | Upload Upload - | Scroll Offset- \ No newline at end of file + | Scroll (Int, Int)+ \ No newline at end of file
diff --git a/src/View.elm b/src/View.elm @@ -1,6 +1,7 @@ module View exposing (view) import Types exposing (..) +import Editor exposing (editor) 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, style, spellcheck, placeholder, target, rel) @@ -9,7 +10,6 @@ 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 @@ -19,7 +19,7 @@ view model = [ div [ id "container", class (if model.isValid then "" else "error") ] [ div [ id "title" ] [ logo, githubLink ] , display model - , texteditor model + , editor model ] ] } @@ -40,39 +40,6 @@ display model = [ if model.darkMode then sunIcon else moonIcon ] ] -texteditor : Model -> Html Msg -texteditor model = - 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 - , onInput Update - , spellcheck False - , 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 uri model = "data:image/svg+xml;utf8," ++ (replace model.uriEncoder percentEscape model.image)
diff --git a/styles.css b/styles.css @@ -14,10 +14,21 @@ scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE + Edge */ + transition: background-color .5s ease, color .5s ease; + --background: #450e79; - --mid: #7b2c9b; + --selection: #3b3ee040; --light: #e0ebe0; --dark: #1c0531; + + --hl1: #d19a66; + --hl2: #f3a47f; + --hl3: #f852a5; + --hl4: var(--hl3); + --hl5: #5da7fc; + --hl6: #d19a66; + + --comm: #95ee61; } @media (pointer: fine) { @@ -44,19 +55,6 @@ width: 0px; /*Chrome + Safari + Opera*/ } -textarea::selection { - background: var(--mid); -} - -textarea::-moz-selection { - background: var(--mid); -} - -textarea::placeholder { - color: var(--light); - opacity: 1; -} - svg { fill: none; fill-rule: evenodd; @@ -158,9 +156,16 @@ body > div#container, body > div#container > * { body > div#container.error { --background: #96162c; - --mid: #b33748; - + --selection: #e03b5140; --light: #eec6df; + + --hl1: #d17666; + --hl2: #f38d7f; + --hl3: #fd5b5b; + --hl5: #fc5db4; + --hl6: #d19a66; + + --comm: #ee6180; } body > div#container img { @@ -187,7 +192,6 @@ body > div#container > div#display { border-radius: var(--tiny); box-shadow: 0 0 calc(.25 * var(--small)) .5vmax #00000015; - transition: background .5s ease; } @media (pointer: fine), (pointer: coarse) and (orientation: landscape) { @@ -249,15 +253,17 @@ body > div#container > div#editor { } body > div#container > div#editor * { - background: transparent !important; - - font-family: 'B612 Mono', monospace !important; - font-style: italic !important; + font-family: 'B612 Mono', monospace; } body > div#container > div#editor > * { position: absolute; + will-change: transform; +} + +body > div#container > div#editor > *, body > div#container > div#editor > * > * { margin: 0 !important; + font-size: 1rem !important; } body > div#container > div#editor > textarea { @@ -269,16 +275,38 @@ body > div#container > div#editor > textarea { width: 100%; height: 100%; - margin: 0; - - background: transparent; border: none; - + background: transparent; - font-family: 'B612 Mono', monospace; color: transparent; - font-style: italic; + caret-color: var(--light); scrollbar-width: none; /*For Firefox*/ -ms-overflow-style: none; /*For IE and Edge*/ -}- \ No newline at end of file +} + +body > div#container > div#editor > textarea::selection { + background: var(--selection); +} + +body > div#container > div#editor > textarea::-moz-selection { + background: var(--selection); +} + +.elmsh {color: var(--light);} +.elmsh-hl {background: rgba(229, 231,235, 0.1);} +.elmsh-add {background: rgba(40, 124,82, 0.4);} +.elmsh-del {background: rgba(136, 64,67, 0.4);} + +.elmsh-comm { + font-style: italic; + color: var(--comm); +} + +.elmsh1 { color: var(--hl1); } +.elmsh2 { color: var(--hl2); } +.elmsh3 { color: var(--hl3); } +.elmsh4 { color: var(--hl4); } +.elmsh5 { color: var(--hl5); } +.elmsh6 { color: var(--hl6); } +.elmsh7 { color: var(--light); }+ \ No newline at end of file