Closed nojaf closed 1 year ago
Certainly, you can suggest something?
Well, I can think of two suggestions.
The first would be introducing another replacement variable that outputs JSON instead of HTML.
{{fsdocs-list-of-documents-json}}
. Having that opens the door to easily constructing HTML on the client.
The second would be to add an option to pass something that can transform https://github.com/nojaf/FSharp.Formatting/blob/82e0c8a09136fa56dcbcd931c290fcce97462970/src/fsdocs-tool/BuildCommand.fs#L553-L586. Maybe an F# script? Or provide an API endpoint, where some data will be posted and the response body is used as the replacement value. I'm not quite sure what could be elegant.
@dsyme, I made an experiment with transforming the menu information in an outside endpoint. https://github.com/fsprojects/FSharp.Formatting/commit/a4d9f8f3fa3acbdb2337a57e35ebab4b48cccbac
I made a small script to act as the receiving endpoint:
#r "nuget: Suave"
#r "nuget: Thoth.Json.Net, 8.0.0"
#r "nuget: Fable.React, 8.0.1"
open System.Net
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Thoth.Json.Net
type Item = {
Title: string
Category: string
CategoryIndex: int
Index: int
Link: string
}
let decodeStringAsInt = Decode.string |> Decode.map (int)
let decodeItem: Decoder<Item> =
Decode.object (fun get -> {
Category = get.Required.Field "category" Decode.string
CategoryIndex = get.Required.Field "categoryIndex" decodeStringAsInt
Index = get.Required.Field "index" decodeStringAsInt
Link = get.Required.Field "link" Decode.string
Title = get.Required.Field "title" Decode.string
}
)
open Fable.React
open Fable.React.Props
let view (items: Item array) : string =
let groups = Array.groupBy (fun i -> i.CategoryIndex) items
let children =
groups
|> Array.map (fun (_, groupItems) ->
let groupTitle = groupItems[0].Category
let id = $"menu-{groupTitle}-collapse".Replace(" ", "-").Trim().ToLower()
let groupItems =
groupItems
|> Array.map (fun (gi: Item) ->
li [] [ a [ Href gi.Link; ClassName "ms-4 my-2 d-block" ] [ str gi.Title ] ]
)
li [ ClassName "mb-1" ] [
button [
ClassName "btn align-items-center rounded"
Data("bs-toggle", "collapse")
Data("bs-target", $"#{id}")
AriaExpanded true
] [ str groupTitle ]
div [ ClassName "collapse show"; Id id ] [
ul [ ClassName "list-unstyled fw-normal pb-1 small" ] groupItems
]
]
)
let element = fragment [] children
Fable.ReactServer.renderToString (element)
let menuPart =
POST
>=> Filters.path "/menu"
>=> (fun (ctx: HttpContext) -> async {
let json = System.Text.Encoding.UTF8.GetString(ctx.request.rawForm)
printfn "received: %s" json
match Decode.fromString (Decode.array decodeItem) json with
| Error err -> return! OK $"<div>Failed to decode, {err}</div>" ctx
| Ok items ->
let html = view items
printfn "html:\n%s" html
return! OK html ctx
}
)
let port = 8906us
startWebServer
{ defaultConfig with
bindings = [ HttpBinding.create HTTP IPAddress.Loopback port ]
}
menuPart
This is quite the escape hatch, but we can do whatever we want inside our endpoint. And I think the impact for FsDocs is minimal. If anything fails, you are on your own, which should be the policy for such a malpractise.
Adding json substitution seems dead simple. Please go ahead with that?
After giving this some more thought and discussing it with a friend, I like to propose an alternative.
What if we work with a _menu_template.html
and _menu-item_template.html
.
Where the default of _menu_template.html
would be:
<li class="nav-header">
{{fsdocs-menu-header-content}}
</li>
{{fsdocs-menu-items}}
and _menu-item_template.html
<li class="nav-item"><a href="{{fsdocs-menu-item-link}}" class="nav-link">{{fsdocs-menu-item-content}}</a></li>
If the additional template files are present, we would transform
accordingly. If not, just return the default behaviour.
Seems reasonable!
Thanks, we will prepare a concrete proposal in a PR!
Hello, we are currently experimenting with FsDoc. We aim to have our own template and are experimenting with Bootstrap 5, Sass and some other ideas.
We are currently hitting a limitation that:
is generating some HTML that doesn't fit our needs. Would there be a way to have some control over what this outputs?
//cc @yisusalanpng