diff options
-rw-r--r-- | data/style.css | 8 | ||||
-rw-r--r-- | elm/DocEdit.elm | 40 | ||||
-rw-r--r-- | elm/Lib/Editsum.elm | 18 | ||||
-rw-r--r-- | elm/Lib/TextPreview.elm | 90 | ||||
-rw-r--r-- | elm/StaffEdit/Main.elm | 21 | ||||
-rw-r--r-- | lib/VNWeb/Misc/BBCode.pm | 9 |
6 files changed, 135 insertions, 51 deletions
diff --git a/data/style.css b/data/style.css index 16378ef5..6027c043 100644 --- a/data/style.css +++ b/data/style.css @@ -98,6 +98,7 @@ b.standout, a.standout { font-weight: normal; color: $standout$; } .clearfloat { clear: both; height: 0; } .hidden { display: none!important } +.invisible { visibility: hidden } .linethrough { text-decoration: line-through } b.spoiler, b.spoiler a { color: #000!important; background-color: #000; font-weight: normal; } b.spoiler_shown { font-weight: normal } @@ -134,7 +135,6 @@ input.text, input.submit, select, textarea { padding: 0 1px 1px 1px; margin: 1px; } -div.preview { border: 1px solid $secborder$; margin: 1px; padding: 5px } form, fieldset { border: 0; display: block } legend { display: none; } optgroup option { padding-left: 10px; font-style: normal; } @@ -144,7 +144,6 @@ fieldset.submit { width: 100%; text-align: center; margin: 5px; } fieldset.submit input[type=submit] { width: 150px; } fieldset.submit input[type=checkbox] { margin: 0 5px 0 15px; } fieldset.submit h2 { font-size: 13px!important; } -fieldset.submit textarea { margin: 0 20px 5px 20px; } td.label, td.label label { width: 130px; } td.label label { display: block; } td.field label { margin: 0 5px 0 5px; } @@ -163,6 +162,11 @@ table.formtable tr.newpart td { padding-top: 20px; font-weight: bold; } div.spinner { content: ''; border: 3px solid #9eaebd; border-bottom-color: transparent; border-radius: 100%; animation: spin 1s infinite linear; width: 14px; height: 14px; display: inline-block; margin: auto } @keyframes spin { from { transform:rotate(0deg); } to { transform:rotate(360deg); } } +.textpreview .head { width: 100%; text-align: right } +.textpreview .head > * { margin-left: 10px; font-style: normal } +.textpreview textarea { width: 100%; box-sizing: border-box } +.textpreview .preview { width: 100%; box-sizing: border-box; border: 1px solid $secborder$; margin: 1px; padding: 5px } +fieldset.submit .textpreview { margin: -15px auto 0 auto } diff --git a/elm/DocEdit.elm b/elm/DocEdit.elm index 1dcd174c..0d654e59 100644 --- a/elm/DocEdit.elm +++ b/elm/DocEdit.elm @@ -7,6 +7,7 @@ import Browser import Browser.Navigation exposing (load) import Json.Encode as JE import Lib.Html exposing (..) +import Lib.TextPreview as TP import Lib.Api as Api import Lib.Ffi as Ffi import Lib.Editsum as Editsum @@ -27,30 +28,28 @@ type alias Model = { state : Api.State , editsum : Editsum.Model , title : String - , content : String + , content : TP.Model , id : Int - , preview : String } init : GD.Recv -> Model init d = { state = Api.Normal - , editsum = { authmod = True, editsum = d.editsum, locked = d.locked, hidden = d.hidden } + , editsum = { authmod = True, editsum = TP.bbcode d.editsum, locked = d.locked, hidden = d.hidden } , title = d.title - , content = d.content + , content = TP.markdown d.content , id = d.id - , preview = "" } encode : Model -> GD.Send encode model = - { editsum = model.editsum.editsum + { editsum = model.editsum.editsum.data , hidden = model.editsum.hidden , locked = model.editsum.locked , title = model.title - , content = model.content + , content = model.content.data } @@ -59,17 +58,15 @@ type Msg | Submit | Submitted GApi.Response | Title String - | Content String - | Preview - | HandlePreview GApi.Response + | Content TP.Msg update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of - Editsum e -> ({ model | editsum = Editsum.update e model.editsum }, Cmd.none) + Editsum m -> let (nm,nc) = Editsum.update m model.editsum in ({ model | editsum = nm }, Cmd.map Editsum nc) Title s -> ({ model | title = s }, Cmd.none) - Content s -> ({ model | content = s }, Cmd.none) + Content m -> let (nm,nc) = TP.update m model.content in ({ model | content = nm }, Cmd.map Content nc) Submit -> let @@ -80,16 +77,6 @@ update msg model = Submitted (GApi.Changed id rev) -> (model, load <| "/d" ++ String.fromInt id ++ "." ++ String.fromInt rev) Submitted r -> ({ model | state = Api.Error r }, Cmd.none) - Preview -> - if model.preview /= "" then ( { model | preview = "" }, Cmd.none ) - else - ( { model | state = Api.Loading, preview = "" } - , Api.post "/js/markdown.json" (JE.object [("content", JE.string model.content)]) HandlePreview - ) - - HandlePreview (GApi.Content s) -> ({ model | state = Api.Normal, preview = s }, Cmd.none) - HandlePreview r -> ({ model | state = Api.Error r }, Cmd.none) - view : Model -> Html Msg view model = @@ -107,14 +94,7 @@ view model = , text " with some " , a [ href "http://fletcher.github.io/MultiMarkdown-5/syntax.html", target "_blank" ][ text "extensions" ] , text "." - , a [ href "#", style "float" "right", onClickN Preview ] - [ text <| if model.preview == "" then "Preview" else "Edit" - , if model.state == Api.Loading then div [ class "spinner" ] [] else text "" - ] - , br_ 1 - , if model.preview == "" - then inputTextArea "content" model.content Content ([rows 50, cols 90, style "width" "850px"] ++ GD.valContent) - else div [ class "docs preview", style "width" "850px", Ffi.innerHtml model.preview ] [] + , TP.view "content" model.content Content 850 ([rows 50, cols 90] ++ GD.valContent) ] ] ] diff --git a/elm/Lib/Editsum.elm b/elm/Lib/Editsum.elm index 7ab1f994..99a4be29 100644 --- a/elm/Lib/Editsum.elm +++ b/elm/Lib/Editsum.elm @@ -6,20 +6,21 @@ module Lib.Editsum exposing (Model, Msg, new, update, view) import Html exposing (..) import Html.Attributes exposing (..) import Lib.Html exposing (..) +import Lib.TextPreview as TP type alias Model = { authmod : Bool , locked : Bool , hidden : Bool - , editsum : String + , editsum : TP.Model } type Msg = Locked Bool | Hidden Bool - | Editsum String + | Editsum TP.Msg new : Model @@ -27,16 +28,16 @@ new = { authmod = False , locked = False , hidden = False - , editsum = "" + , editsum = TP.bbcode "" } -update : Msg -> Model -> Model +update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of - Locked b -> { model | locked = b } - Hidden b -> { model | hidden = b } - Editsum s -> { model | editsum = s } + Locked b -> ({ model | locked = b }, Cmd.none) + Hidden b -> ({ model | hidden = b }, Cmd.none) + Editsum m -> let (nm,nc) = TP.update m model.editsum in ({ model | editsum = nm }, Cmd.map Editsum nc) view : Model -> Html Msg @@ -60,6 +61,5 @@ view model = [ text "Edit summary" , b [class "standout"] [text " (English please!)"] ] - -- TODO: BBCode preview - , inputTextArea "editsum" model.editsum Editsum [rows 4, cols 50, minlength 2, maxlength 5000, required True] + , TP.view "" model.editsum Editsum 600 [rows 4, cols 50, minlength 2, maxlength 5000, required True] ] diff --git a/elm/Lib/TextPreview.elm b/elm/Lib/TextPreview.elm new file mode 100644 index 00000000..8c9e3287 --- /dev/null +++ b/elm/Lib/TextPreview.elm @@ -0,0 +1,90 @@ +module Lib.TextPreview exposing (..) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Json.Encode as JE +import Lib.Html exposing (..) +import Lib.Ffi as Ffi +import Lib.Api as Api +import Gen.Api as GApi + + +type alias Model = + { state : Api.State + , data : String -- contents of the textarea + , preview : String -- Rendered HTML, "" if not in sync with data + , display : Bool -- False = textarea is displayed, True = preview is displayed + , apiUrl : String + , class : String + } + + +bbcode : String -> Model +bbcode data = + { state = Api.Normal + , data = data + , preview = "" + , display = False + , apiUrl = "/js/bbcode.json" + , class = "preview bbcode" + } + + +markdown : String -> Model +markdown data = + { state = Api.Normal + , data = data + , preview = "" + , display = False + , apiUrl = "/js/markdown.json" + , class = "preview docs" + } + + +type Msg + = Edit String + | TextArea + | Preview + | HandlePreview GApi.Response + + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = + case msg of + Edit s -> ({ model | preview = "", data = s, display = False, state = Api.Normal }, Cmd.none) + TextArea -> ({ model | display = False }, Cmd.none) + + Preview -> + if model.preview /= "" + then ( { model | display = True }, Cmd.none) + else ( { model | display = True, state = Api.Loading } + , Api.post model.apiUrl (JE.object [("content", JE.string model.data)]) HandlePreview + ) + + HandlePreview (GApi.Content s) -> ({ model | state = Api.Normal, preview = s }, Cmd.none) + HandlePreview r -> ({ model | state = Api.Error r }, Cmd.none) + + +view : String -> Model -> (Msg -> m) -> Int -> List (Attribute m) -> Html m +view name model cmdmap width attr = + let + display = model.display && model.preview /= "" + in + div [ class "textpreview", style "width" (String.fromInt width ++ "px") ] + [ p (class "head" :: (if model.data == "" then [class "invisible"] else [])) + [ case model.state of + Api.Loading -> div [ class "spinner" ] [] + Api.Error _ -> b [ class "grayedout" ] [ text "Error loading preview. " ] + Api.Normal -> text "" + , if display + then a [ onClickN (cmdmap TextArea) ] [ text "Edit" ] + else i [] [text "Edit"] + , if display + then i [] [text "Preview"] + else a [ onClickN (cmdmap Preview) ] [ text "Preview" ] + ] + , inputTextArea name model.data (cmdmap << Edit) (class (if display then "hidden" else "") :: attr) + , if not display then text "" + else div [ class model.class, Ffi.innerHtml model.preview ] [] + ] diff --git a/elm/StaffEdit/Main.elm b/elm/StaffEdit/Main.elm index 909cfd82..2eeaea27 100644 --- a/elm/StaffEdit/Main.elm +++ b/elm/StaffEdit/Main.elm @@ -7,6 +7,7 @@ import Browser import Browser.Navigation exposing (load) import Lib.Util exposing (..) import Lib.Html exposing (..) +import Lib.TextPreview as TP import Lib.Api as Api import Lib.Editsum as Editsum import Gen.StaffEdit as GSE @@ -29,7 +30,7 @@ type alias Model = , alias : List GSE.RecvAlias , aliasDup : Bool , aid : Int - , desc : String + , desc : TP.Model , gender : String , lang : String , l_site : String @@ -44,11 +45,11 @@ type alias Model = init : GSE.Recv -> Model init d = { state = Api.Normal - , editsum = { authmod = d.authmod, editsum = d.editsum, locked = d.locked, hidden = d.hidden } + , editsum = { authmod = d.authmod, editsum = TP.bbcode d.editsum, locked = d.locked, hidden = d.hidden } , alias = d.alias , aliasDup = False , aid = d.aid - , desc = d.desc + , desc = TP.bbcode d.desc , gender = d.gender , lang = d.lang , l_site = d.l_site @@ -67,7 +68,7 @@ new = , alias = [ { aid = -1, name = "", original = "", inuse = False } ] , aliasDup = False , aid = -1 - , desc = "" + , desc = TP.bbcode "" , gender = "unknown" , lang = "ja" , l_site = "" @@ -81,12 +82,12 @@ new = encode : Model -> GSE.Send encode model = - { editsum = model.editsum.editsum + { editsum = model.editsum.editsum.data , hidden = model.editsum.hidden , locked = model.editsum.locked , aid = model.aid , alias = List.map (\e -> { aid = e.aid, name = e.name, original = e.original }) model.alias - , desc = model.desc + , desc = model.desc.data , gender = model.gender , lang = model.lang , l_site = model.l_site @@ -114,7 +115,7 @@ type Msg | LTwitter String | LAnidb String | LPixiv String - | Desc String + | Desc TP.Msg | AliasDel Int | AliasName Int String | AliasOrig Int String @@ -129,7 +130,7 @@ validate model = { model | aliasDup = hasDuplicates <| List.map (\e -> (e.name, update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of - Editsum m -> ({ model | editsum = Editsum.update m model.editsum }, Cmd.none) + Editsum m -> let (nm,nc) = Editsum.update m model.editsum in ({ model | editsum = nm }, Cmd.map Editsum nc) Lang s -> ({ model | lang = s }, Cmd.none) Gender s -> ({ model | gender = s }, Cmd.none) Website s -> ({ model | l_site = s }, Cmd.none) @@ -137,7 +138,7 @@ update msg model = LTwitter s -> ({ model | l_twitter = s }, Cmd.none) LAnidb s -> ({ model | l_anidb = if s == "" then Nothing else String.toInt s }, Cmd.none) LPixiv s -> ({ model | l_pixiv = Maybe.withDefault model.l_pixiv (String.toInt s) }, Cmd.none) - Desc s -> ({ model | desc = s }, Cmd.none) + Desc m -> let (nm,nc) = TP.update m model.desc in ({ model | desc = nm }, Cmd.map Desc nc) AliasDel i -> (validate { model | alias = delidx i model.alias }, Cmd.none) AliasName i s -> (validate { model | alias = modidx i (\e -> { e | name = s }) model.alias }, Cmd.none) @@ -204,7 +205,7 @@ view model = [ h1 [] [ text "General info" ] , table [ class "formtable" ] [ formField "Names" [ names, br_ 1 ] - , formField "desc::Biography#eng" [ inputTextArea "desc" model.desc Desc GSE.valDesc ] + , formField "desc::Biography#eng" [ TP.view "desc" model.desc Desc 500 GSE.valDesc ] , formField "gender::Gender" [ inputSelect "gender" model.gender Gender [] [ ("unknown", "Unknown or N/A") , ("f", "Female") diff --git a/lib/VNWeb/Misc/BBCode.pm b/lib/VNWeb/Misc/BBCode.pm new file mode 100644 index 00000000..09501cb8 --- /dev/null +++ b/lib/VNWeb/Misc/BBCode.pm @@ -0,0 +1,9 @@ +package VNWeb::Misc::BBCode; + +use VNWeb::Prelude; + +json_api '/js/bbcode.json', { + content => { required => 0, default => '' } +}, sub { + elm_Content bb2html bb_subst_links shift->{content}; +}; |