Skip to content

Commit

Permalink
bump version
Browse files Browse the repository at this point in the history
  • Loading branch information
AngelMunoz committed Mar 6, 2024
1 parent 83d24f4 commit c099bdb
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 6 deletions.
217 changes: 212 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,215 @@
# An experimental router for dotnet Avalonia code-first applications
# The Navs Family

This router is kind of inspired by the angular/vue/navigo router. For sure it still needs work but for the moment it is kind of alive.
This is a monorepo that contains a few projects that work closely together but each is available as a separate library.

There are very rough edges and the design is... well it is not designed at all. It is just a bunch of code that works for the moment.
- [Navs](#Navs)
- [UrlTemplates](#UrlTemplates)
- [Navs.Avalonia](#Navs-Avalonia)
- [Navs.FuncUI](#Navs-FuncUI)

(that last sentence is courtesy of GH Copilot, it has some kind of sense of humor lol)
While this is being currently tested with Avalonia, it ideally will be extensible enough to be used elsewhere.
### Navs

Navs is a router-like abstraction inspired by web routers such as vue-router, angular-router and similar projects.

It is primarily a "core" library which you would usually depend on in your own projects, as it is very generic and while F# can be very intelligent about type inference, it tends to produce quite verbose signatures. For more information visit the Navs section in these docs.

- [Navs](https://angelmunoz.github.io/Navs/Navs.html)

A Compelling Example:

```fsharp
let routes = [
Route.define<string>("home", "/", (fun _ -> "Home")
Route.define<string>("about", "/about", (fun _ -> "About"))
Route.define<string>(
"guid",
"/:id<guid>",
fun context -> async {
do! Async.Sleep(90)
return
match context.UrlMatch.Params.TryGetValue "id" with
| true, id -> sprintf "Home %A" id
| false, _ -> "Guid No GUID"
}
)
]
let router = Router<string>(RouteTracks.fromDefinitions routes)
router.content.Subscribe(fun content -> printfn $"%A{content}")
let! result1 = router.navigate("/about")
let! result2 = router.navigate("/home")
let! result3 = router.navigate("/123e4567-e89b-12d3-a456-426614174000")
// "About"
// "Home"
// "Home 123e4567-e89b-12d3-a456-426614174000"
```

### Navs.Avalonia

This project attempts to hide the generics from call sites and offer a few DSLs to make it easier to use Navs in Avalonia applications. This router was designed to be used with Raw Avalonia Control classes however, it will pair very nicely with the [NXUI](https://github.com/wieslawsoltes/NXUI) project, Feel free to check the C# and F# samples in the [Samples](https://github.com/AngelMunoz/Navs/tree/main/samples) folder in the source code repository.

- [Navs.Avalonia](https://angelmunoz.github.io/Navs/Navs-Avalonia.html)

A Compelling Example:

```fsharp
let routes = [
Route.define(
"guid",
// routes can be typed!
"/:id<guid>",
fun context -> async {
// you can pre-load data if you want to
do! Async.Sleep(90)
return
// extract parameters from the URL
match context.UrlMatch.Params.TryGetValue "id" with
| true, id -> TextBlock().text(sprintf "Home %A" id)
| false, _ -> TextBlock().text("Guid No GUID")
}
)
// Simpler non-async routes are also supported
Route.define("books", "/books", (fun _ -> TextBlock().text("Books")))
]
let getMainContent (router: AvaloniaRouter) =
ContentControl()
.DockTop()
// with NXUI you can use the .content method to bind the content
// to the observable in a seamless way
.content(router.Content.ToBinding(), BindingMode.OneWay)
let navigate url (router: AvaloniaRouter) _ _ =
task {
// navigation is asynchronous and returns a result
// in order to check if the navigation was successful
let! result = router.Navigate(url)
match result with
| Ok _ -> ()
| Error e -> printfn $"%A{e}"
}
|> ignore
let app () =
let router = AvaloniaRouter(routes, splash = fun () -> TextBlock().text("Loading..."))
Window()
.content(
DockPanel()
.lastChildFill(true)
.children(
StackPanel()
.DockTop()
.OrientationHorizontal()
.spacing(8)
.children(
Button().content("Books").OnClickHandler(navigate "/books" router),
Button()
.content("Guid")
.OnClickHandler(navigate $"/{Guid.NewGuid()}" router)
),
getMainContent(router)
)
)
NXUI.Run(app, "Navs.Avalonia!", Environment.GetCommandLineArgs()) |> ignore
```

### Navs.FuncUI

In a similar Fashion of Navs.Avalonia, this project attempts to provide a smooth API interface for [Avalonia.FuncUI](https://github.com/fsprojects/Avalonia.FuncUI/), you can find a sample in the [Samples](https://github.com/AngelMunoz/Navs/tree/main/samples) folder in the source code repository.

- [Navs.FuncUI](https://angelmunoz.github.io/Navs/Navs-FuncUI.html)

A Compelling Example:

```fsharp
let routes = [
Route.define(
"books",
"/books",
(fun _ -> TextBlock.create [ TextBlock.text "Books" ])
)
Route.define(
"guid",
"/:id<guid>",
fun context -> async {
return
TextBlock.create [
match context.UrlMatch.Params.TryGetValue "id" with
| true, id -> TextBlock.text $"Visited: {id}"
| false, _ -> TextBlock.text "Guid No GUID"
]
}
)
]
let appContent (router: FuncUIRouter, navbar: FuncUIRouter -> IView) =
Component(fun ctx ->
let currentView = ctx.useRouter router
DockPanel.create [
DockPanel.lastChildFill true
DockPanel.children [ navbar router; currentView.Current ]
]
)
```

### UrlTemplates

This is a library for parsing URL-like strings into structured objects. It is used by Navs to parse navigable URLs and URL templates to find if they match.

Currently this library is mainly aimed to be used from F# but if there's interest in using it from C# I can add some more friendly APIs.

- [UrlTemplates](https://angelmunoz.github.io/Navs/UrlTemplates.html)

A Compelling Example:

```fsharp
open UrlTemplates.RouteMatcher
let template = "/api/v1/profiles/:id<int>?optionalKey<guid>&requiredKey!#hash"
let url = "/api/v1/profiles/2345?requiredKey=2#hash"
match RouteMatcher.matchStrings template url with
| Ok (urlTemplate, urlInfo, urlMatch) ->
let { Segments = foundParams; Query = queryParams; Hash = foundHash } = urlTemplate
// foundParams
// [ Plain ""; Plain "api"; Plain "v1"; Plain "profiles"; Param ("id", "2345");]
// query
// [Optional "optionalKeyu", Guid; Required "requiredKey", Int]
// hash
// "hash"
let { Params = urlParams; Query = query; Hash = hash } = urlInfo
// urlParams
// [ ""; "api"; "v1"; "profiles"; "2345" ]
// query
// [ "optionalKey", String ValueNone; "requiredKey", String ValueSome "2"]
// hash
// ValueSome "hash"
let { Params = foundParams; QueryParams = queryParams; Hash = foundHash } = urlMatch
// foundParams
// { { "id", box 2345 } }
// queryParams
// { { "requiredKey", box "2" } }
// foundHash
// ValueSome "hash"
| Error errors ->
for e in errors do
printfn $"%A{e}"
```
2 changes: 1 addition & 1 deletion build.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
open System.IO
open Fun.Build

let version = "1.0.0-beta-001"
let version = "1.0.0-beta-002"


let build name = stage $"Build {name}" { run $"dotnet build src/{name}" }
Expand Down

0 comments on commit c099bdb

Please sign in to comment.